poj2594 Simpsons’ Hidden Talents(KMP | 扩展KMP)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_43408238/article/details/102669941

题意:给你两个字符串s 和 t,让你找一个最长的字串,它是s的前缀 ,也是t的后缀,如果没有输出0,否则输出这个字符串和最大的长度。

思路:很基础的KMP,一开始没转过来这个弯,主要是对KMP算法中f[ ]  数组的含义不是很清楚。

f[ i ] 的含义 为 B中 以 i 结尾的子串 与 模式串 A的前缀能够匹配的最长长度。

所以本题中,以s为模式串,t为文本串 求出 f[ ] 数组即可,f[n] 即为所求子串的最大长度。

AC Code:

#include<iostream>
#include<cstring>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<cstdio>
#include<iomanip>
#include<sstream>
#include<algorithm>

using namespace std;
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define sRead(x,y,z)  scanf("%d%d%d",&x,&y,&z)
#define gc(x)  scanf(" %c",&x);
#define mmt(x,y)  memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod  998244353
#define pdd pair<double,double>
const int N = 1000;
const int M=  1e6;
char s[M+5];
char c[M+5];
int Next[M+5];
int f[M+5];
void kmp_pre(int m){
    int i = 0,j = Next[0] = -1;
    while(i < m){
        while(j!= -1&&s[i] != s[j]) j = Next[j];
        Next[++i] = ++j;
    }
}
void kmp(int n,int m)
{
    int j  = 0,i = 0;
    while(i < n){
        while(j!=-1&&s[j]!=c[i]) j = Next[j];
        f[++i] = ++j;
        //if(j >=m) j = Next[j];
    }
}
int main()
{
    while(~scanf("%s%s",s,c)){
        mmt(Next,0);
        mmt(f,0);
        int n = strlen(s);
        int m = strlen(c);
        kmp_pre(n);
        kmp(m,n);
        int MAX = f[m];
        if(MAX == 0){
            puts("0");
        }else {
            cout<<(c+m - MAX);
            cout<<" "<<MAX<<endl;
        }
    }
}

扩展KMP

Next[ ] 数组的含义 :

Next[ i ] 含义是A中以 i 结尾的后缀与前缀匹配最大的长度。

extend[ ] 数组的含义 :extend[i] 的含义 是 B 中以 i 结尾的后缀与A字符串前缀匹配的最大长度

所以这个题目只需要求出 extend数组 ,对于 每一个位置 i 来说 , extend[i] + i == len(B) ,说明 B存在长度为 extend[i] 的 后缀 与 A 中长度为 extend[i] 的前缀 匹配。那么只需要从左向右遍历一遍,检查到跳出即可。

AC Code:

#include<iostream>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<cmath>
#include<cstdio>
#include<iomanip>
#include<sstream>
#include<algorithm>

using namespace std;
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define sRead(x,y,z)  scanf("%d%d%d",&x,&y,&z)
#define gc(x)  scanf(" %c",&x);
#define mmt(x,y)  memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod  998244353
#define pdd pair<double,double>
const int N = 1000;
const int M=  1e6+5;
char s[M+5],t[M+5];
int Next[M+5];
int extend[M+5];
void pre_EKMP(char x[],int m,int Next[])
{
	Next[0]=m;
	int j=0;
	while(j+1<m && x[j]==x[j+1]) j++;
	Next[1]=j;
	int k=1;
	for(int i=2;i<m;i++)
	{
		int p=Next[k]+k-1;
		int L=Next[i-k];
		if(i+L<p+1)Next[i]=L;
		else
		{
			j=max(0,p-i+1);
			while(i+j<m && x[i+j]==x[j])j++;
			Next[i]=j;
			k=i;
		}
	}
}
void EKMP(char x[],int m,char y[],int n,int Next[],int extend[])
{
	pre_EKMP(x,m,Next);
	int j=0;
	while(j<n && j<m && x[j]==y[j]) j++;
	extend[0]=j;
	int k=0;
	for(int i=1;i<n;i++)
	{
		int p=extend[k]+k-1;
		int L=Next[i-k];
		if(i+L<p+1)
			extend[i]=L;
		else
		{
			j=max(0,p-i+1);
			while(i+j<n && j<m && y[i+j]==x[j]) j++;
			extend[i]=j;
			k=i;
		}
	}
}
int main()
{
    while(~scanf("%s%s",s,t)){
        mmt(Next,0);
        mmt(extend,0);
        int m,n;
         m = strlen(s);
         n = strlen(t);
         EKMP(s,m,t,n,Next,extend);//求出 extend[] 
        // for(int i = 0;i < n;++i) cout<<extend[i]<<" ";
         int k = -1;
         for(int i = 0;i < n;++i){
            if(extend[i] + i == n)//遍历 ,找到跳出
                    {k = i;break;}
         }

        // continue;
         if(k == -1) {
            puts("0");
            continue;
         }

         cout<<(t + k)<<" ";
         cout<<extend[k]<<endl;

    }
}

猜你喜欢

转载自blog.csdn.net/qq_43408238/article/details/102669941