HDU 5510 Bazinga

1. 笔记
思路是:首先从前到后扫一遍,如果一个字符串不是后一个字符串的字串就把它标记一下(表示之后不会再扫它);再从后到前来看,很容易想到,如果从k到n都被未被标记过,而k-1被标记过,那么答案肯定是[k,n]里的一个数(当然,输出-1当且仅当k==1)。另外,如果答案是ans,那么[k,ans]都满足条件,(ans,n]都不满足条件。
我看网上很多题解都是从暴力从n扫到1,甚至还有用strstr也A的。但k=n/2时复杂度是(O(n^2l))。保险起见我用KMP+二分,复杂度是(O(nlognl))
2. 代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define ms(arr,a) memset(arr,a,sizeof arr)
#define debug(x) cout<<"< "#x" = "<<x<<" >"<<endl
const int maxP=2005;
int f[maxP];
void get_fail(char P[])
{
    int l=strlen(P);
    f[0]=f[1]=0;
    for(int i=1,j;i<l;++i)
    {
        j=f[i];
        while(j&&P[i]!=P[j])j=f[j];
        f[i+1]=P[i]==P[j]?j+1:0;
    }
}
char* kmp(char T[],char P[])
{
    get_fail(P);
    int lT=strlen(T),lP=strlen(P);
    for(int i=0,j=0;i<lT;++i)
    {
        while(j&&T[i]!=P[j])j=f[j];
        if(T[i]==P[j])++j;
        if(j==lP)return T+(i-lP+1);
    }
    return NULL;
}

int n;
char s[505][2005];
int mark[505]={0};
bool ok(int m)
{
    for(int i=mark[m];i;)
    {
        if(!kmp(s[m],s[i]))return true;
        if(mark[i]==i)i--;
        else i=mark[i];
    }
    return false;
}
int bs(int l,int r)
{
    int m;bool okl=ok(l),okr=ok(r);
    if(okl&&okr)return r;
    if(!okl&&!okr)return -1;
    if(!okl){r=r^l;l=r^l;r=r^l;}
    while(r-l>1||l-r>1)
    {
        m=(l+r)/2;
        if(ok(m))l=m;
        else r=m;
    }
    return l;
}
int main()
{
    //freopen("Input.txt","r",stdin);
	int T;scanf("%d",&T);
	for(int Case=1;Case<=T;++Case)
    {
        scanf("%d",&n);
        rep(i,1,n)scanf("%s",s+i);
        rep(i,1,n-1)mark[i]=kmp(s[i+1],s[i])?mark[i-1]:i;mark[n]=mark[n-1];
        //rep(i,1,n)printf("%d ",mark[i]);printf("
");
        printf("Case #%d: %d
",Case,bs(mark[n-1]+1,n));//
    }
}
原文地址:https://www.cnblogs.com/maoruimas/p/9579937.html