【JZOJ 4964】 Rhyme

小Z因集邮受挫转而挑战音乐部创作难题。他希望创作的曲谱能符合特定长度子串的要求,通过图论与拓扑排序解决最长曲谱问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值