【雜湊&KMP模板題】-POJ-3461-Oulipo
題目描述:給一個子串再給一個主串,問子串在主串中出現了多少次。裸題一道,用來試驗雜湊和kmp模板
解題思路&AC程式碼:
(哎呀我去。。這題都要背下來了 = =
①--HASH
首先,我覺得雜湊好寫還好理解,先寫了一個雜湊的程式碼,所謂雜湊就是給字串生成一個雜湊值,也就是算出一個數來,用這個數來代表這個字串,顯然對數的操作都是o(1)的,所以就達到了減小時間複雜度的作用。
有幾個問題應該說明一下,因為一共就26個字母,所以取31進位制比較好,然後這要是錯了的話就改成1e6+7啥的試試,一般錯不了,算雜湊值的時候通常為了方便比較還會搞出一個hash陣列來,計算這個陣列的時候從後面往前算能使計算方便一點。需要再次強調的是雜湊值只能“儲存”這個字串的資訊,因為通常沒幾下就爆了ll,取模後的值就沒什麼數學意義了,千萬不要拿雜湊值去做“數學計算”!
不多說了。。貼程式碼
#include <iostream> #include <cstring> #include <cstdio> using namespace std; typedef unsigned long long ll; const int base = 31; const int maxn = 1000050; char sub[maxn],str[maxn]; ll xp[maxn]; ll hash[maxn]; int main() { int T,i; scanf("%d",&T); xp[0]=1; for(i=1;i<maxn;i++) xp[i]=xp[i-1]*base; while(T--) { memset(sub,0,sizeof(sub)); memset(str,0,sizeof(str)); scanf("%s",sub); scanf("%s",str); int L=strlen(sub); int n=strlen(str); ll sub_num=0; for(i=L-1;i>=0;i--) { sub_num=sub_num*base+sub[i]; } hash[n]=0; for(i=n-1;i>=0;i--) { hash[i]=hash[i+1]*base+str[i]; } int ans=0; for(i=0;i<=n-L;i++) ///Caution!!! it is (i<=n-L) or (i<n-L+1) { if(sub_num==hash[i]-hash[i+L]*xp[L]) ans++; } printf("%d\n",ans); } return 0; }
②--KMP
接下來是用KMP方法解決匹配問題,這個演算法有點難以理解,我到現在也沒完全搞明白,先拿模板這麼用吧,且敲且理解。。。
子串與主串匹配的時候,一旦失配,樸素的演算法上子串回溯到頭,主串往後走一位,這是有浪費的
比如:
P: a b c a b e
S:a b c a b k a b c
e和k比失配了,我們沒必要樸素演算法那樣回溯,而是把S串中c那個位置對準到k那個位置繼續比較,這樣做是因為ab先匹配上後面才有戲,所以KMP演算法就搞出了一個next陣列,next【i】存放的是一旦到 i 失配,我們該回溯到子串的什麼位置。
如何計算出next陣列是KMP演算法的難點,事實上我們可以理解為next【i】代表了 i 以前的字串的“自我重合度”。求next【i】的過程就是找 i 前面字串的“字首、字尾最長重合長度”。我們設 i , j 變數分別表示前後綴的最後字元位置,相等,那麼歡天喜地都加一,失配,那麼 i 回溯到next【i】,用這樣一種遞推似的思想來求出整個next陣列。
以上理解說實話我心裡也沒底。。要多敲題再體會體會。。唉。。講得太快了,每節例會的內容我覺得都夠搞一個星期的。。。明天比賽了,不知道能不能進省賽,,加油吧~
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1000100;
char p[maxn],s[maxn];
int next[maxn];
int lenp,lens;
int KMP()
{
int i=0,j=0,ans=0;
while(i<lenp&&j<lens)
{
if(i==-1||p[i]==s[j])
{
i++;j++;
}
else
{
i=next[i];
}
if(i==lenp)
{
ans++;
i=next[i];
}
}
return ans;
}
void getnext()
{
next[0]=-1;
int i=0,j=-1;
while(i<lenp)
{
if(j==-1||p[i]==p[j])
{
i++;j++;
if(p[i]==p[j])next[i]=next[j];
else next[i]=j;
}
else
{
j=next[j];
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",p);
scanf("%s",s);
lenp=strlen(p);
lens=strlen(s);
getnext();
/*
for(int i=0;i<lenp;i++)
cout<<next[i]<<" ";
cout<<endl;
*/
int ans=KMP();
printf("%d\n",ans);
}
return 0;
}