Description
由于多次交换邮票没有满足所有人的需求,小Z被赶出了集邮部。无处可去的小Z决定加入音乐部,为了让音乐部的人注意到自己的才华,小Z想写一首曲子。为了让自己的曲子更好听,小Z找到了一些好听曲子作为模板。曲谱可以表示成只包含小写字母的字符串,小Z希望自己最终的曲谱中任意一个长度为K的子串都是一个模板的子串。现在小Z想知道自己的曲谱最长可以是多长,如果可以无限长的话请输出INF。
对于100%的数据:每组数据字符串总长不超过100000,1≤K≤100000。每个测试点数据不超过10组。
Analysis
比赛时想到了类正解
由于有一个固定的长度k,所以字符串哈希大法好
对于每个长度k-1的子串,从它向它整体右移一格的子串连边,说明这两个串可以接起来
然后会形成一个图,若有环就肯定INF
否则就是DAG上的经典问题,拓扑排序跑一边即可
Code
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,v) for(int i=last[v];i;i=next[i])
using namespace std;
typedef unsigned long long ll;
const int N=100005,hx=2000000;
const ll mo=9223372036854775807;
int n,k,tot,ans,to[N],next[N],last[hx],in[hx],f[hx];
ll h[hx];
bool bz[hx];
queue<int> q;
char s[N];
ll qmi(ll x,ll n)
{
ll t=1;
for(;n;n>>=1)
{
if(n&1) t=t*x%mo;
x=x*x%mo;
}
return t;
}
ll hash(ll x)
{
ll pos=x%hx;
while(h[pos] && h[pos]!=x) pos=(pos+1)%hx;
h[pos]=x;
return pos;
}
void link(int u,int v)
{
in[v]++,bz[u]=bz[v]=1;
to[++tot]=v,next[tot]=last[u],last[u]=tot;
}
void topo()
{
while(!q.empty())
{
int u=q.front();q.pop();
ans=max(ans,f[u]);
efo(i,u)
{
int v=to[i];
f[v]=max(f[v],f[u]+1);
if(!(--in[v])) q.push(v);
}
}
}
int main()
{
freopen("rhyme.in","r",stdin);
freopen("rhyme.out","w",stdout);
int T;
while(scanf("%d %d\n",&T,&k)!=EOF)
{
ll _k=qmi(26,k-1);
tot=1;
memset(last,0,sizeof(last));
memset(in,0,sizeof(in));
memset(bz,0,sizeof(bz));
while(T--)
{
scanf("%s\n",s+1);
n=strlen(s+1);
if(n<k) continue;
fo(i,1,n) s[i]=s[i]-'a'+1;
ll t=0;
fo(i,1,k-1) t=t*26+s[i];
fo(i,k,n)
{
ll u=hash(t);
t=t*26+s[i]-s[i-k+1]*_k;
ll v=hash(t);
link(u,v);
}
}
ans=k-1;
fo(i,0,hx-1)
if(bz[i] && !in[i]) q.push(i),f[i]=k-1;
topo();
bool p=0;
fo(i,0,hx-1)
if(in[i]) {p=1;break;}
if(p) printf("INF\n");
else printf("%d\n",ans);
}
return 0;
}