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));//
}
}