KMP算法 Next數組詳解

題面

題目描述

如題,給出兩個字符串s1和s2,其中s2爲s1的子串,求出s2在s1中全部出現的位置。html

爲了減小騙分的狀況,接下來還要輸出子串的前綴數組next。若是你不知道這是什麼意思也不要問,去百度搜[kmp算法]學習一下就知道了。
輸入輸出格式ios

輸入格式:

第一行爲一個字符串,即爲s1(僅包含大寫字母)算法

第二行爲一個字符串,即爲s2(僅包含大寫字母)數組

輸出格式:

若干行,每行包含一個整數,表示s2在s1中出現的位置學習

接下來1行,包括length(s2)個整數,表示前綴數組next[i]的值。spa

輸入樣例:

ABABABC
ABAcode

輸出樣例:

1
3
0 0 1htm

說明

時空限制:1000ms,128M
數據規模:
設s1長度爲N,s2長度爲M
對於30%的數據:N<=15,M<=5
對於70%的數據:N<=10000,M<=100
對於100%的數據:N<=1000000,M<=1000blog

題解

這是一道KMP裸題(模板題。。)
我就是拿着它學習一下KMP算法
其實原來我學過KMP算法
可是一直沒有弄懂next(跳轉)數組是如何求出來的。
最近花了一個下午本身研究了一下KMP算法
如今終於以爲KMP很簡單了~




如今直接說next數組把
至於有什麼做用,next數組是幹什麼的,請自行百度,有不少dalao總結的很是到位,看一看就會明白。


好,來講next數組


這裏寫圖片描述


並不用在乎這一坨黑的是什麼東西,咱們就假設他是咱們要求next數組的字符串。


next數組求的東西就是從起始位置到當前位置最長的相等的前綴和後綴的長度。
(舉個例子China的前綴有:C、Ch、Chi、Chin、China ; 後綴有a、na、ina、hina、China)圖片

這裏寫圖片描述




咱們繼續,如上圖紅色的是當前位置(設爲j)前,所匹配上的最長前綴和後綴,藍色的是當前要匹配的位置。

這裏寫圖片描述

那麼,咱們就拿當前位置和原來匹配到的最長前綴的後一位相比較
若是兩個位置相同,
顯然,
能夠和前面的紅色連在一塊兒,
此時就有next[j]=next[j-1]+1

若是兩個位置不相同,
根據next數組的性質,
顯然的,你的當前的相等的前綴和後綴只可以繼續向前找,
也就是說,你當前的next數組必定會減少。

這裏寫圖片描述


既然前面的紅色部分存在一小塊灰色,那麼,後面的紅色部分也必然存在灰色部分。


這裏寫圖片描述




因此,判斷當前位置和前面那一塊灰色的前綴的後一位是否相等。
若是這兩位相同的話,不就能夠和前面的灰色部分連在一塊兒了嗎


這裏寫圖片描述

此時,又回到一開始的那一步。
所以,求解某個位置的next值是一個循環過程。
不斷檢查 上一位的 最長前綴的 後一位(i位置)(這句子有點拗口)
若是相等next[j]=next[i]+1
不然令 i=next[i-1]+1,繼續循環匹配

若是沒有看懂就本身多看幾遍,本身找幾個字符串算一算

因此:求解next數組的代碼:

inline void GetNext(string s)//得到字符串s的next數組
{
    int l=s.length(),t;
    Next[0]=-1;//若是在0位置失配則是向下移動一位
    for(int i=1;i<l;++i)//依次求解後面的next數組
    {
        t=Next[i-1];
        while(s[t+1]!=s[i]&&t>=0)//循環求解next值 
            t=Next[t];
        if(s[t+1]==s[i])//若是是匹配上而退出循環 
            Next[i]=t+1;
        else            //不然則是匹配不上 
            Next[i]=-1; //指向頭 
    }
}




代碼很簡潔的~
那麼,接下來如何利用Next數組求解匹配
那就本身baidu吧(知道了next數組,KMP就很好理解了)

接下來貼上小蒟蒻的源碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int MAX=1001;
int Next[MAX];
vector<int> Ans;
inline void GetNext(string s)//得到字符串s的next數組
{
    int l=s.length(),t;
    Next[0]=-1;//若是在0位置失配則是向下移動一位
    for(int i=1;i<l;++i)//依次求解後面的next數組
    {
        t=Next[i-1];
        while(s[t+1]!=s[i]&&t>=0)//循環求解next值 
            t=Next[t];
        if(s[t+1]==s[i])//若是是匹配上而退出循環 
            Next[i]=t+1;
        else            //不然則是匹配不上 
            Next[i]=-1; //指向頭 
    }
}
inline void KMP(string s1,string s2)
{
    GetNext(s2);
    int l1=s1.length();
    int l2=s2.length();
    int i=0,j=0;
    while(j<l1)
    {
        if(s2[i]==s1[j])//當前位匹配成功,繼續匹配下一位
        {
            ++i;++j;
            if(i==l2)//徹底匹配
            {
                Ans.push_back(j-l2+1);//儲存答案
                i=Next[i-1]+1;//繼續匹配                
            }
        }
        else
        {
            if(i==0)//在首位不匹配
                j++;//直接向後挪一位
            else
                i=Next[i-1]+1;//跳轉
        }
    }
}
int main()
{
    string s1,s2;
    int l;
    cin>>s1>>s2;
    l=s2.length();
    KMP(s1,s2);
    for(int i=0;i<Ans.size();++i)
        cout<<Ans[i]<<endl;
    for(int i=0;i<l;++i)
        cout<<Next[i]+1<<' ';
    cout<<endl;
    return 0;
}

最後再說一句
若是須要加深理解KMP的過程
請去SYC的blog看看他的gif動圖
你可能就會有更多瞭解
膜拜SYC大佬去

相關文章
相關標籤/搜索