【GDKOI2017模拟1.21】Rhyme

题目

Description

由于多次交换邮票没有满足所有人的需求,小Z被赶出了集邮部。无处可去的小Z决定加入音乐部,为了让音乐部的人注意到自己的才华,小Z想写一首曲子。为了让自己的曲子更好听,小Z找到了一些好听曲子作为模板。曲谱可以表示成只包含小写字母的字符串,小Z希望自己最终的曲谱中任意一个长度为K的子串都是一个模板的子串。现在小Z想知道自己的曲谱最长可以是多长,如果可以无限长的话请输出INF。

Input

本题的每个测试点有多组数据,对于每组数据:
第一行两个整数N,K分别表示模板的个数与K值。
接下来N行,每行一个字符串表示一个模板。(只包含’a’~’z’)

Output

对于每组数据输出一行表示曲子最长可以是多长,如果可以无限长的话输出INF。

Sample Input

1 4
abcabc
1 5
abcabc

Sample Output

INF
6

Data Constraint

对于30%的数据:K=2。
对于70%的数据:每组数据字符串总长不超过1000。
对于100%的数据:每组数据字符串总长不超过100000,1≤K≤100000。每个测试点数据不超过10组。

Hint

第一个样例的曲子可以是”abc”不断循环。
第二个样例的曲子最长是”abcabc”。

题解

一开始觉得这道题应该是SAM之类的高级算法题,然而后来发现是一道套路题
首先我们可以把每一个长度为K-1的字符串看成是一个节点,然后对于一个输入中的字符串,显然是可以从s[1..k-1]向S[2..K]连接一条有向边的,那么我们就得到了一个有向图
现在的问题就变成了
1:这个有向图是否有环
2:如果没有环,那么从一个节点出发,最多可以走多少个点
如果第一个问题成立那么答案显然是INF
对于第二个问题考虑做一次DAG
字符串转成数字可以用双hash

贴代码

const h1=103007; h2=21478647;
var
    h:array[0..200005]of int64;
    a,b:array[0..200005,1..2]of longint;
    a1,dp,now,next:array[0..200005]of longint;
    bz:array[0..200005]of boolean;
    s:array[0..100005]of longint;
    i,j,k,l,m,n,x,y,z,c1,c2,d1,d2,p,ans,e1,e2:longint;
    ch:char;
function max(x,y:longint):longint;
begin
    if x>y then exit(x) else exit(y);
end;
function hash(c1,c2:longint):longint;
var
    x:longint;
begin
    x:=c1;
    while (h[x]<>c2) and (h[x]<>0) do x:=(x+1) mod h1;
    h[x]:=c2;
    {while ((h[x,1]<>c1) or (h[x,2]<>c2)) do
    begin
        if (h[x,1]=0) and (h[x,2]=0) then
        begin
            h[x,1]:=c1;
            h[x,2]:=c2;
            break;
        end;
        x:=x mod h1+1;
    end;  }
    exit(x);
end;
procedure qsort(l,r:longint);
var
    i,j,mid:longint;
begin
    i:=l;
    j:=r;
    mid:=a[(i+j) div 2,1];
    repeat
        while a[i,1]<mid do inc(i);
        while a[j,1]>mid do dec(j);
        if i<=j then
        begin
            a[0]:=a[i];
            a[i]:=a[j];
            a[j]:=a[0];
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then qsort(i,r);
    if l<j then qsort(l,j);
end;
procedure star;
begin
    b[a[1,1],1]:=1;
    for i:=2 to z do
    if a[i,1]<>a[i-1,1] then
    begin
        b[a[i-1,1],2]:=i-1;
        b[a[i,1],1]:=i;
    end;
    b[a[z,1],2]:=z;
end;
procedure work;
begin
    readln(n,m);
    if n=0 then exit;
    fillchar(dp,sizeof(dp),0);
    fillchar(s,sizeof(s),0);
    fillchar(h,sizeof(h),0);
    fillchar(bz,sizeof(bz),false);
    fillchar(a1,sizeof(a1),0);
    fillchar(a,sizeof(a),0);
    fillchar(b,sizeof(b),0);
    z:=0;
    for i:=1 to n do
    begin
        c1:=0;
        c2:=0;
        e1:=1;
        e2:=1;
        p:=0;
        j:=1;
        while not eoln do
        begin
            read(ch);
            inc(p); s[p]:=ord(ch)-96;
            inc(j);
            if j=m then break;
        end;
        {if j<m-1 then
        begin
            readln;
            writeln(m);
            exit;
        end; }
        for j:=m-1 downto 1 do
        begin
            c1:=(c1+e1*s[j]) mod h1;
            c2:=(c2+e2*s[j]) mod h2;
            if j=1 then break;
            e1:=(e1*26) mod h1;
            e2:=(e2*26) mod h2;
        end;
        if (c1<0) or (c2<0) then
        begin
            c1:=c1;
        end;
        x:=hash(c1,c2);
        //if ord(ch)-96<>c1 then writeln('!');
        while not eoln do
        begin
            read(ch);
            inc(p); s[p]:=ord(ch)-96;
            c1:=(c1-(s[p-m+1]*e1) mod h1+h1) mod h1;
            c1:=(c1*26+s[p]) mod h1;
            c2:=(c2-(s[p-m+1]*e2) mod h2+h2) mod h2;
            c2:=(c2*26+s[p]) mod h2;
            y:=hash(c1,c2);
            inc(z);
            a[z,1]:=x;
            a[z,2]:=y;
            x:=y;
        end;
        //if ord(ch)-96<>c1 then writeln('!');
        readln;
    end;
    if m=1 then
    begin
        writeln('INF');
        exit;
    end;
    qsort(1,z);
    star;
    p:=0;
    if n=10 then
    begin
        ans:=ans;
    end;
    for i:=1 to z do
    begin
        if bz[a[i,1]]=false then
        begin
            bz[a[i,1]]:=true;
            inc(p);
        end; // else writeln(chr(a[i,1]+96));
        if bz[a[i,2]]=false then
        begin
            bz[a[i,2]]:=true;
            inc(p);
        end; //else writeln(chr(a[i,2]+96));
        inc(a1[a[i,2]]);
    end;
    now[0]:=0;
    for i:=1 to z do
    if (a1[a[i,1]]=0) and (bz[a[i,1]]=true) then
    begin
        inc(now[0]);
        now[now[0]]:=a[i,1];
        bz[a[i,1]]:=false;
        dec(p);
    end;
    ans:=0;
    while p>0 do
    begin
        if now[0]=0 then
        begin
            writeln('INF');
            break;
        end;
        next[0]:=0;
        for i:=1 to now[0] do
        begin
            x:=now[i];
            for j:=b[x,1] to b[x,2] do
            if j<>0 then
            begin
                dec(a1[a[j,2]]);
                if a1[a[j,2]]=0 then
                begin
                    {if bz[a[j,2]]=false then
                    begin
                        ans:=ans;
                    end;
                    bz[a[j,2]]:=false;}
                    inc(next[0]);
                    next[next[0]]:=a[j,2];
                    dec(p);
                end;
                dp[a[j,2]]:=max(dp[a[j,2]],dp[a[j,1]]+1);
                ans:=max(ans,dp[a[j,2]]);
            end;
        end;
        for i:=0 to next[0] do now[i]:=next[i];
    end;
    if p=0 then writeln(ans+m-1);
end;
begin
    //assign(input,'t2.in'); reset(input);
    assign(input,'rhyme.in'); reset(input);
    assign(output,'rhyme.out'); rewrite(output);
    while not eof do work;
    close(input); close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值