hdu 4763 Theme Section(KMP)

题意:在一个字符串中,找出一个最长的子串,该子串满足条件:在串的开头、中间和结尾分别出现一次且不能交叉重叠。
思路:KMP求出next,确定串的前缀和后缀的公共长度,再在中间部分查找是否存在满足条件的子串。

将串分为3段进行查找,如图,


-----------------      ----------------------      i ------------------

其中,第三部分从i坐标开始。

        m=_next[len];//m代表最大的首尾相同长度
        ans=0;
        for(int i=len-1;i>=2;--i){
            k=_next[i];//k为i之前与开头重复的长度
            while(k>0){
                if(k<=m&&k+k<=i&&i+k<=len){//长度小于m,且三段不重合
                    if(k>ans)ans=k;
                    break;//当前是最大的长度
                }
                k=_next[k];//next[k]一定小于k
            }
        }

在查找过程中,通过以下约束条件判断:其中k<=m使当前得到的长度不超过最大的首尾相同长度。k+k<=i使第一段和第二段的长度和小于i,避免串的交叉重叠。i+k<=len使第三段满足条件。
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

#define MAXN 1000005//字符串长度

char str[MAXN];

int _next[MAXN];

void GetNext(char t[]){//求next数组
    int j,k,len;
    j=0;//从0开始,首先求_next[1]
    k=-1;//比较指针
    _next[0]=-1;//初始值-1
    len=strlen(t);
    while(j<len){
        if(k==-1||t[j]==t[k]){//指针到头了,或者相等
            ++j;
            ++k;
            _next[j]=k;//此句可由优化替代
            /*优化(求匹配位置时可用)
            if(t[j]!=t[k])_next[j]=k;
            else _next[j]=_next[k];
            //*/
        }
        else k=_next[k];
    }
}

int main(){

    int N;
    int len;
    int m,k,ans;//m代表最大的首尾相同长度,k为i之前与开头重复的长度

    scanf("%d",&N);

    while(N--){
        scanf("%s",str);
        GetNext(str);

        len=strlen(str);
        m=_next[len];//m代表最大的首尾相同长度
        ans=0;
        for(int i=len-1;i>=2;--i){
            k=_next[i];//k为i之前与开头重复的长度
            while(k>0){
                if(k<=m&&k+k<=i&&i+k<=len){//长度小于m,且三段不重合
                    if(k>ans)ans=k;
                    break;//当前是最大的长度
                }
                k=_next[k];//next[k]一定小于k
            }
        }
        printf("%d\n",ans);
    }

    return 0;
}

你可能感兴趣的:(hdu 4763 Theme Section(KMP))