1. 程式人生 > >hdu2087-自己的理解&kmp講解-剪花布條

hdu2087-自己的理解&kmp講解-剪花布條

http://acm.hdu.edu.cn/showproblem.php?pid=2087
連結
時隔多年,終於理解kmp了。。
1 進行字串匹配,如果進行暴力匹配的話,(即一個一個找,時間複雜度O(len1*len2),是不可取的,)
2 所以需要改進這個演算法。 如何改進,這個問題,引起了三個年輕小夥子的注意,有一天他們在一個小溪邊玩,突然發現了一個老太太在磨針,並且針的 頭和尾各有一部分相同的花紋,就好像金箍棒的兩頭一樣。然後,三個人之間的小k突發奇想,在紙上畫了這麼一個圖。
這裡寫圖片描述
他發現,大人們在進行暴力匹配的時候,當出現了藍色的情況(紅色是目標串,下面是模式串,如果前面的綠色相同,那麼模式串就可以跳過去,跳過前面的比較。)
小m看了看,朗聲道,想法不錯,咋寫啊。那個綠色的怎麼表示??
小p說,好辦,你有沒有發現,這就是以i為最後一個字元 的 模式串的子串 的 最長的 相同字首和字尾()??上面那個是字尾,蝦米那那個是 字首,(字尾頂到後,字首要頂到前,實在不會,就先去學習 字尾陣列qwq)
小m又說,如果轉移又匹配失敗了呢。。還要從頭開始?
這時,小p說,一看你c語言課就沒好好聽,這不是老師常說的遞迴的思想麼,如果匹配失敗,就讓 這個 最長的 相同字首和字尾 當成一個 子串,繼續遞迴尋找他的 ,判斷 他的相同字首和字尾 後面的那個元素是否和 藍色字母相同。如果相同就可以,如果不同在遞迴。直到沒有,就從頭再來。
說時遲,那時快,小k馬上畫了一個圖。重人恍然大悟,笑嘻嘻的回家了。
這裡寫圖片描述

next[i]儲存的 1————i-1 這個串的最長 相同 字首和字尾,
遍歷的過程,如果每次找到都取0,就是統計個數。(相當於找到一個後,母串子串都在當前左對齊,重新找。。qwq)
演算法思想: 字串的前後綴,問題轉化為更小的問題(dp??遞迴?,反正就是醬紫)
附贈一個 程式碼 和kuangbin神的 模板。

 #include <iostream>
#include <bits/stdc++.h>
using namespace std;
/*
1 關於next陣列的計算。
kmp之所以能夠簡化速度,是因為記錄了模式串的最長公共字首和字尾
*/
int
nex[105];//計算 next陣列, // 並且 nex[i] 是 i-1串的 最長的(字首和字尾相同的)長度 int ans; void next(char s[]){ //memset(nex,-1,sizeof(nex)); int len=strlen(s); //cout<<"len "<<len<<endl; int i=0;//下面就要計算 next陣列了 int k=-1;//記錄最大next nex[0]=-1; while(i<len){ if(k==-1
||s[i]==s[k]){ i++;k++; nex[i]=k;//記住,nex i是i-1串的 最長前後綴。 } else k=nex[k];//遞迴,在最長 前後綴中間繼續尋找。 } } void kmp(char a[],char b[]){ next(b); int len1=0; int len2=0; int siz1=strlen(a); int siz2=strlen(b); while(len1<siz1){ while(len2>0&&a[len1]!=b[len2]){ len2=nex[len2]; }// 第一次位置, if(a[len1]==b[len2]) {len2++;} if(len2==siz2){ ans++; //len2=nex[len2];// 尋找出現的次數。 len2=0;// 讓模式串從頭開始找。 } len1++; } } int main() { char a[1003],b[1003]; while(~scanf("%s",a)){ ans=0; if(strcmp(a,"#")==0){ break; } scanf("%s",b); kmp(a,b); printf("%d\n",ans); } return 0; }
kuangbin 模板
#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000002;
int next[N];
char S[N], T[N];
int slen, tlen;

void getNext()
{
    int j, k;
    j = 0; k = -1; next[0] = -1;
    while(j < tlen)
        if(k == -1 || T[j] == T[k])
            next[++j] = ++k;
        else
            k = next[k];
            for(int i=0;i<tlen;i++)
                cout<<next[i]<<" "<<endl;

}
/*
返回模式串T在主串S中首次出現的位置
返回的位置是從0開始的。
*/
int KMP_Index()
{
    int i = 0, j = 0;
    getNext();

    while(i < slen && j < tlen)
    {
        if(j == -1 || S[i] == T[j])
        {
            i++; j++;
        }
        else
            j = next[j];
    }
    if(j == tlen)
        return i - tlen;
    else
        return -1;
}
/*
返回模式串在主串S中出現的次數
*/
int KMP_Count()
{
    int ans = 0;
    int i, j = 0;

    if(slen == 1 && tlen == 1)
    {
        if(S[0] == T[0])
            return 1;
        else
            return 0;
    }
    getNext();
    for(i = 0; i < slen; i++)
    {
        while(j > 0 && S[i] != T[j])
            j = next[j];
        if(S[i] == T[j])
            j++;
        if(j == tlen)
        {
            ans++;
            j = next[j];
        }
    }
    return ans;
}
int main()
{

    int TT;
    int i, cc;
    cin>>TT;
    while(TT--)
    {
        cin>>S>>T;
        slen = strlen(S);
        tlen = strlen(T);
        cout<<"模式串T在主串S中首次出現的位置是: "<<KMP_Index()<<endl;
        cout<<"模式串T在主串S中出現的次數為: "<<KMP_Count()<<endl;
    }
    return 0;
}