hdu4763 Theme Section(KMP)

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

题意 :给你一个字符串,然后让你找一个子串,这个子串需要作为前缀、后缀 和 在中间出现。输出这个子串的最大长度。

思路:依旧是Next 数组的性质。首先明白Next数组的含义,Next[i] 表示 以 i 结尾的非前缀子串与前缀匹配的最大长度。

所以,整个字符串的后缀入手,如果Next[m] = 0,则肯定不存在这种子串,输出0即可。否则 Next[m] 肯定等于等于一个数,假设Next[m] = k ,表示该字符串前缀 与 后缀能匹配的长度,然后在 从 前面的位置中查看是否有Next[i] = k,如果有则k就是这个 最大长度。

不过需要特判一种情况,那就是形如 aaaaa这种字符串,前中后平分这个字符串,直接输出 m / 3即可。如果没出现在样例中,我真的可能想不起这种情况,后怕。。

!!!上述思路 有两处不够完善的地方

一个是Next[m]只是表示这个字符串前缀和后缀能匹配的最大长度,所以如果中间不存在最大长度,按照上述思路就输出0,可是最大长度不存在,那么次大长度呢?次次大长度呢?并没有判断,所以还得从最大到最小枚举检验。

二是我没有考虑这个题目这三个子串有没有交叉,显然在 本题中,是不可以 有重叠的,所以检验的时候区间范围应在去除前缀长度和后缀长度的中间检验。

修改后的 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];
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;
    }
}
bool ok(int k,int m){
    for(int i = 2*k;i <= m - k;++i){//区间在中间
        if(Next[i] == k) return 1;
    }
    return 0;
}
int main()
{
    int T;
    read(T);
    while(T--){
        mmt(Next,0);
        scanf("%s",s);
        int m = strlen(s);
        bool op = 1;
        for(int i = 1;i < m;++i){
            if(s[i] != s[1]) {
                op = 0;
                break;
            }
        }
        if(op == 1) {
           cout<<m / 3<<endl;
           continue;
        }
        kmp_pre(m);
        int k = Next[m];
        if(k == 0) puts("0");
        else {
            bool r = 1;
            while(k != -1){//枚举前缀和后缀匹配的所有子串
                if(ok(k,m)) {r = 0;break;}
                else k = Next[k];
            }
            if(r == 1) puts("0");
            else cout<<k<<endl;
        }
    }
}

猜你喜欢

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