擴展KMP題目總結

問題定義:給定兩個字符串S和T(長度分別爲n和m),下標從0開始,定義extend[i]等於S[i]...S[n-1]與T的最長相同前綴的長度,求出所有的extend[i]

顯然,如果在S的某個位置i有extend[i]等於m,則可知在S中找到了匹配串T,並且匹配的首位置是i。

模板 

#include <iostream>
#include <string>

using namespace std;

/* 求解 T 中 next[],註釋參考 GetExtend() */
void GetNext(string & T, int & m, int next[])
{
    int a = 0, p = 0;
    next[0] = m;

    for (int i = 1; i < m; i++)
    {
        if (i >= p || i + next[i - a] >= p)
        {
            if (i >= p)
                p = i;

            while (p < m && T[p] == T[p - i])
                p++;

            next[i] = p - i;
            a = i;
        }
        else
            next[i] = next[i - a];
    }
}

/* 求解 extend[] */
void GetExtend(string & S, int & n, string & T, int & m, int extend[], int next[])
{
    int a = 0, p = 0;
    GetNext(T, m, next);

    for (int i = 0; i < n; i++)
    {
        if (i >= p || i + next[i - a] >= p) // i >= p 的作用:舉個典型例子,S 和 T 無一字符相同
        {
            if (i >= p)
                p = i;

            while (p < n && p - i < m && S[p] == T[p - i])
                p++;

            extend[i] = p - i;
            a = i;
        }
        else
            extend[i] = next[i - a];
    }
}

int main()
{
    int next[100];
    int extend[100];
    string S, T;
    int n, m;
    
    while (cin >> S >> T)
    {
        n = S.size();
        m = T.size();
        GetExtend(S, n, T, m, extend, next);

        // 打印 next
        cout << "next:   ";
        for (int i = 0; i < m; i++)
            cout << next[i] << " ";
 
        // 打印 extend
        cout << "\nextend: ";
        for (int i = 0; i < n; i++)
            cout << extend[i] << " ";

        cout << endl << endl;
    }
    return 0;
}

aaaaabbb
aaaaac
next:   6 4 3 2 1 0
extend: 5 4 3 2 1 0 0 0

abc
def
next:   3 0 0
extend: 0 0 0

 

 

題意:

每組給你兩個字符串s1和s2,求s2的所有後綴在s1中出現的頻率,頻率再乘以對應的後綴的長度,累加。

思路:

我們將s1,s2字符串翻轉,然後進行一次擴展KMP,得到extend數組,然後對extend數組上的每一個值進行等差數列計算,就是最後結果。

#include<algorithm>
#include<iostream>
#include<limits.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#define MAXN 1000001
#define mod 1000000007
typedef long long ll;
using namespace std;
ll extend[MAXN],next1[MAXN];
int t;
char S[MAXN],T[MAXN];
void GetNext(int Tlen)
{
    int a,p,j;
    next1[0]=Tlen;
    for(int i=1,j=-1;i<Tlen;i++,j--)
    {
        if(j<0||i+next1[i-a]>=p)
        {
            if(j<0)
            {
                j=0;
                p=i;
            }
            while (p<Tlen&&T[j]==T[p])//這個地方爲啥是p<len呢?因爲在這個地方,p是S串的下標,j是T串的下標,但這個是求next數組的,所以S和T相等,p與len的距離只可能等於或者小於j與len的距離,不可能大於
            {
                p++;
                j++;
            }
            next1[i]=j;
            a=i;
        }
        else
            next1[i]=next1[i-a];
    }
}
void GetExtend(int Slen,int Tlen)
{
    GetNext(Tlen);
    int a,p,j;
    for(int i=0,j=-1;i<Slen;i++,j--)
    {
        if(j<0||i+next1[i-a]>=p)
        {
            if(j<0)
            {
                j=0;
                p=i;
            }
            while (p<Slen&&j<Tlen&&T[j]==S[p])
            {
                p++;
                j++;
            }
            extend[i]=j;
            a=i;
        }
        else
            extend[i]=next1[i-a];
    }
}
int main()
{
    cin>>t;
    while (t--)
    {
        scanf("%s%s",S,T);
        memset(next1,0,sizeof(next1));
        memset(extend,0,sizeof(extend));
        int Slen=strlen(S);
        int Tlen=strlen(T);
        reverse(S,S+Slen);//將S字符串和T字符串倒置
        reverse(T,T+Tlen);
        GetExtend(Slen,Tlen);//只需要過一遍擴展KMP就可以。
        ll sum=0;
        for(int i=0;i<Slen;i++)
            sum=(sum+((1+extend[i])*extend[i]/2)%mod)%mod;
        cout<<sum<<endl;
    }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章