1. 程式人生 > >【雜湊&KMP模板題】-POJ-3461-Oulipo

【雜湊&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;
}