題解:AT_abc345_c [ABC345C] One Time Swap

whrwlx發表於2024-03-25

求過審

題面翻譯

給定一個字串 $ s $ ,求執行以下操作一次可以產生的字串的個數

設 $ N $ 為 $ s $ 的長度。選擇一對整數 $ (i,j) $ ,使 $ 1≤i<j≤N $ ,交換 $ s $ 的第 $ i $ 個和第 $ j $ 個字元

可以證明,在這個問題的約束條件下,你總是可以得到它

思路

暴力做法

我們可以暴力列舉 $ 1 \leq i < j \leq N $

若 $ s_i=s_j $ ,即交換後 $ s $ 不變,那麼跳過

否則 $ ans $ 加 $ 1 $

注意:如果有重複字母,需要將 $ ans $ 加 $ 1 $

#include<bits/stdc++.h>
#define ll long long
using namespace std;
string s;
ll l,ans=0,f=0;
int main()
{
	ios::sync_with_stdio(0);//關閉流同步 
	cin>>s;
	l=s.size();
	s=' '+s;//巧妙地把第一位下標轉成1
	for(int i=1;i<=l;i++)
	{
		for(int j=i+1;j<=l;j++)
		{
			if(s[i]!=s[j]) ans++;
			else f=1;
		}
	}
	cout<<ans+f;
	return 0;
}

提交後發現 $ TLE\ 13 $ 個點

最佳化

我們可以發現對於 $ s_i $

那麼和其相等的 $ s_j $ 的個數可以透過字尾和統計

於是

for(int j=i+1;j<=l;j++)
{
	if(s[i]!=s[j]) ans++;
	else f=1;
}

這重迴圈可以變為

ans+=l-i+1sum[s[i]-'a'+1];
//這裡是把a的下標看做1
//sum是字尾和

所以我們可以先求字尾和,然後計算

也可以倒著列舉,邊計算邊求

這裡採用倒著列舉:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
string s;
ll f[1000100],a[100100],l,fl;
//fl記錄是否有重複字母
int main()
{
	cin>>s;
	l=s.size();
	s=' '+s;
	a[s[l]-'a'+1]=1;
	f[l]=0;
	for(ll i=l-1;i>=1;--i)
	{
		a[s[i]-'a'+1]++;
		if(!fl&&a[s[i]-'a'+1]>1) fl=1;
		f[i]=l-i+1-a[s[i]-'a'+1];
	}
	for(ll i=1;i<=l;++i) f[0]+=f[i];
	cout<<f[0]+fl;
	return 0;
}

無關的話(稽核大大不要計較):被禁言了,怎麼解啊?

感謝觀看

相關文章