poj1743 Musical Theme 后缀数组

题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:
1.长度至少为5个音符。
2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)
3.重复出现的同一主题不能有公共部分。
二分答案长度为k,然后用SA判断是否符合情况,每个height<k且sa之间的距离一定要大于k。
打了半天一直WA,结果最后发现是清空的时候要全部清空不是只清空n以内的= =,醉了。

//uses math;
var
    l,r,mid,i,j,k,p,n,m,ans:longint;
    s,b,c,d,sa,rank,height:Array[0..400005]of longint;
function max(x,y:longint):longint;
begin
        if x>y then exit(x)
        else exit(y);
end;
function min(x,y:longint):longint;
begin
        if x<y then exit(x)
        else exit(y);
end;
procedure getsa(m:longint);
var
    i,j,t:longint;
begin
    for i:=0 to m do b[i]:=0;
    for i:=1 to n do inc(b[s[i]]);
    for i:=1 to m do b[i]:=b[i]+b[i-1];
    for i:=n downto 1 do
    begin
        c[b[s[i]]]:=i;
        dec(b[s[i]]);
    end;
    t:=0;
    for i:=1 to n do
    begin
        if s[c[i]]<>s[c[i-1]]then inc(t);
        rank[c[i]]:=t;
    end;
    j:=1;
    while j<=n do
    begin
        for i:=0 to n do b[i]:=0;
        for i:=1 to n do inc(b[rank[i+j]]);
                for i:=1 to n do b[i]:=b[i]+b[i-1];
        for i:=n downto 1 do
        begin
            c[b[rank[i+j]]]:=i;
            dec(b[rank[i+j]]);
        end;
        for i:=0 to n do b[i]:=0;
        for i:=1 to n do inc(b[rank[i]]);
                for i:=1 to n do b[i]:=b[i]+b[i-1];
                for i:=n downto 1 do
        begin
            d[b[rank[c[i]]]]:=c[i];
            dec(b[rank[c[i]]]);
        end;
        t:=0;
        for i:=1 to n do
        begin
            if (rank[d[i]]<>rank[d[i-1]])or
                        ((rank[d[i]]=rank[d[i-1]])and(rank[d[i]+j]<>rank[d[i-1]+j]))then inc(t);
            c[d[i]]:=t;
        end;
        for i:=1 to n do rank[i]:=c[i];
        if t=n then break;
        j:=j*2;
    end;
    for i:=1 to n do sa[rank[i]]:=i;
end;
procedure getheight;
var
    i,j,t,k:longint;
begin
    k:=0;
    for i:=1 to n do
    begin
        if k<>0 then dec(k);
        j:=sa[rank[i]-1];
        while (i+k<=n)and(j+k<=n)and(s[i+k]=s[j+k])do inc(k);
        height[rank[i]]:=k;
    end;
end;
function check(k:longint):boolean;
var
    i,j,mn,mx:longint;
begin
        mn:=maxlongint;
    mx:=0;
    for i:=1 to n do
    begin
        if height[i]<k then
        begin
            mn:=maxlongint;
            mx:=0;
            continue;
        end;
        mn:=min(mn,min(sa[i],sa[i-1]));
        mx:=max(mx,max(sa[i],sa[i-1]));
        if mx-mn>k then exit(true);
    end;
    exit(false);
end;
begin
        while true do
        begin
                readln(n);
                if n=0 then break;
                for i:=0 to 400000 do
                begin
                        sa[i]:=0;
                        rank[i]:=0;
                        height[i]:=0;
                end;

            for i:=1 to n do read(s[i]);
            for i:=1 to n-1 do s[i]:=s[i]-s[i+1]+100;
            s[n]:=0;
            getsa(200);
            getheight;
            l:=4;
            r:=n;
            ans:=0;
            while l<=r do
            begin
                mid:=(l+r)div 2;
                if check(mid) then
                begin
                    ans:=mid+1;
                    l:=mid+1;
                end
                else r:=mid-1;
            end;
            writeln(ans,'         ');
         end;
end.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值