烏力波

shaoyufei發表於2024-04-24

佩雷克很可能在下面的比賽中得到高分(當然,也有可能是低分)。在這個比賽中,人們被要求針對一個主題寫出甚至是意味深長的文章,並且讓一個給定的“單詞”出現次數儘量少。我們的任務是給評委會編寫一個程式來數單詞出現了幾次,用以得出參賽者最終的排名。參賽者經常會寫一長串廢話,例如500000個連續的‘T’。並且他們不用空格。

因此我們想要儘快找到一個單詞出現的頻數,即一個給定的字串在文章中出現了幾次。更加正式地,給出字母表{'A','B','C',...,'Z'}和兩個僅有字母表中字母組成的有限字串:單詞W和文章T,找到W在T中出現的次數。這裡“出現”意味著W中所有的連續字元都必須對應T中的連續字元。T中出現的兩個W可能會部分重疊。

輸入格式
輸入包含多組資料。

輸入檔案的第一行有一個整數,代表資料組數。接下來是這些資料,以如下格式給出:

第一行是單詞W,一個由{'A','B','C',...,'Z'}中字母組成的字串,保證1<=|W|<=10000(|W|代表字串W的長度)

第二行是文章T,一個由{'A','B','C',...,'Z'}中字母組成的字串,保證|W|<=|T|<=1000000。

輸出格式
對每組資料輸出一行一個整數,即W在T中出現的次數。

樣例
樣例輸入
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
樣例輸出
1
3
0

單查詢會T

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
ull mod=1234567891;
int prime=97; 
int n;
ull a;
string s1,s2;
int shash(const string s){
	int res=0;
	int len=s.length();
	for(int i=0;i<len;i++){
		res=((ll)res*prime+s[i])%mod;
	}
	return res;
} 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int ans=0;
		cin>>s1>>s2;
		int num=shash(s1),len1=s1.length(),len2=s2.length();
		for(int i=0;i<len2-len1+1;i++){
			string s3;
			for(int j=i;j<=i+len1-1;j++){
				s3+=s2[j];
			}
			if(shash(s3)==num){
				ans++; 
			}
		}
		cout<<ans<<endl;
	}
}

後面看了題解,公式看不懂:
\(f_i(s)\)表示f(s[1…i])的雜湊值
所以會有\(f_i(s)=s[1]*base^{i-1}+s[2]*base^{i-2}+…+s[i]\)
所以得出
\(f(s[l…r])=f_r(s)-f_{l-1}(s)*base^{r-l+1}\)
感覺還是看圖明白
image
所以思路就是預處理base的n次方,第二個串1~n的雜湊值,透過上面公式求出來與第一個串長度相同的子串的雜湊值

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
ull mod=1234567891;
int prime=97; 
int n;
ull a;
char s1[1200000],s2[1200000];
ull base[1200010];
ull num2[1200010];
ull check(int l,int r){
	return (ull)num2[r]-base[r-l+1]*num2[l-1];
}
int main(){
	cin>>n;
	base[0]=1;
	for(int i=1;i<=1000000;i++) base[i]=base[i-1]*prime;
	for(int i=1;i<=n;i++){
		int ans=0;
		scanf("%s%s",s1+1,s2+1);
		int len1=strlen(s1+1),len2=strlen(s2+1);
		ull num=0;
		for(int j=1;j<=len1;j++){
			num=num*prime+s1[j];
		}
		for(int j=1;j<=len2;j++){
			num2[j]=num2[j-1]*prime+s2[j];
		}
		for(int j=1;j<=len2-len1+1;++j){
			if(check(j,j+len1-1)==num) ans++;
		}
		cout<<ans<<endl;
	}
}

相關文章