【USACO2.3.1】【洛谷P1470】最长前缀【KMP】

题目大意:

题目链接:

USACO:http://train.usaco.org/usacoprob2?a=dLE0hVDUyv1&S=prefix
洛谷:https://www.luogu.org/problemnew/show/P1470

给出多个子串和一个字符串,求该字符串的前多少位可以完全被子串覆盖掉。


思路:

很多人都说用 D P DP DP和搜索,但是我怎么看都是 K M P KMP KMP
我们可以在 O ( n ) O(n) O(n)的时间复杂度内求出一个元素在 S S S序列里的位置,那么可以用前缀和的思想,用一个数组记录答案,找到一个位置后,将头的位置 i i i的答案 a n s i + + ans_i++ ansi++,尾的位置 j j j的答案 a n s j + + ans_j++ ansj++。对所有元素进行一边改操作,时间复杂度 O ( n m ) O(nm) O(nm),其中 m m m表示元素个数。
然后以 a n s ans ans跑一遍前缀和,此时如果 a n s ans ans k k k个数大于 0 0 0,答案就是 k k k


代码:

#include 
#include 
#include 
using namespace std;

const int M=210;
const int N=200100;
int n,m,j,sum=1,next[20],ans[N];
char a[N],b[M][20],c[M];

int main()
{
    while (cin>>b[sum]+1&&b[sum][1]!='.') sum++;
    while (cin>>c)  //太菜不知道有什么更好的读入方法
        for (int i=0;i<strlen(c);i++)
            a[++n]=c[i];
    for (int k=1;k<sum;k++)
    {
        memset(next,0,sizeof(next));
        m=strlen(b[k]+1);
        j=0;
        next[1]=0; 
        for (int i=1;i<m;i++)  //求next
        {
            while (j&&b[k][j+1]!=b[k][i+1]) j=next[j];
            if (b[k][j+1]==b[k][i+1]) j++;
            next[i+1]=j;
        }
        j=0;
        for (int i=0;i<n;i++)  //KMP
        {
            while (j&&b[k][j+1]!=a[i+1]) j=next[j];
            if (b[k][j+1]==a[i+1]) j++;
            if (j==m)
            {
                ans[i+2]--;
                ans[i-m+2]++;
                j=next[j];
            }
        }
    }
    for (int i=1;i<=n;i++)
        ans[i]+=ans[i-1];  //跑一遍前缀和
    for (int i=1;i<=n;i++)
        if (ans[i]<=0)
        {
            printf("%d\n",i-1);
            return 0;
        }
    printf("%d\n",n);
    return 0;
}

你可能感兴趣的:(KMP,字符串)