【题意】
一篇n个音符的乐谱,求他最长的音乐主题
乐谱由n个小于88的正整数表示
所谓的音乐主题就是两端变化相同的长度大于五的不重合乐谱
比如1 2 3 4 5 6 7 8 9 10
1 2 3 4 5的变化与6 7 8 9 10就相同,且两端不重合
【输入】
多组数据
每组数据第一行为n,表示音符的个数
接下来n个数字表示各个音符
输入以n=0结束
【输出】
对于每组数据输出一个数表示最长的音乐主题,音乐主题不存在输出0
后缀数组的典型应用
09年论文《后缀数组——处理字符串的有力工具》上的例题
大意是先对变化求height,然后二分答案,将问题转化为证明是否存在长度为k的不重合序列
根据后缀数组的性质可知height数组是呈波峰状的,所以按height大于k的条件将其分组,若有一组中最小的sa和最大的sa之差大于k,则存在。
需要注意的是,n=1的情况。
program poj1743; var n,i,j,k,s,e,mid,min,max:longint; dl,root,rank,sa,height,total,now,change,keep:array [-1..20001] of longint; ok:boolean; procedure qsort (s,e:longint); var i,j,k,o:longint; begin if s>=e then exit; i:=s; j:=e; k:=dl[(s+e) div 2]; while i<=j do begin while root[dl[i]]<root[k] do inc(i); while root[dl[j]]>root[k] do dec(j); if i>j then break; o:=dl[i]; dl[i]:=dl[j]; dl[j]:=o; inc(i); dec(j); end; qsort(s,j); qsort(i,e); end; begin repeat read(n); if n=0 then break; dec(n); for i:=0 to n do read(root[i]); if n=0 then begin writeln(0); continue; end; for i:=n downto 1 do root[i]:=root[i]-root[i-1]; for i:=1 to n do dl[i]:=i; qsort(1,n); k:=1; for i:=1 to n do if root[dl[i]]=root[dl[k]] then rank[dl[i]]:=k else begin k:=i; rank[dl[i]]:=k; end; k:=0; while 1 shl k<n do begin for i:=1 to n do if i+(1 shl k)>n then change[i]:=0 else change[i]:=rank[i+(1 shl k)]; fillchar(total,sizeof(total),0); for i:=1 to n do inc(total[change[i]]); for i:=1 to n do total[i]:=total[i-1]+total[i]; for i:=1 to n do begin dl[total[change[i]-1]+1]:=i; inc(total[change[i]-1]); end; fillchar(total,sizeof(total),0); ok:=true; for i:=1 to n do begin inc(total[rank[i]]); if total[rank[i]]=2 then ok:=false; end; if ok then break; for i:=2 to n do total[i]:=total[i]+total[i-1]; fillchar(now,sizeof(now),0); fillchar(keep,sizeof(keep),0); for i:=1 to n do begin if change[dl[i]]<>now[rank[dl[i]]] then begin now[rank[dl[i]]]:=change[dl[i]]; total[rank[dl[i]]-1]:=total[rank[dl[i]]-1]+keep[rank[dl[i]]]; keep[rank[dl[i]]]:=0; end; inc(keep[rank[dl[i]]]); rank[dl[i]]:=total[rank[dl[i]]-1]+1; end; inc(k); end; for i:=1 to n do sa[rank[i]]:=i; fillchar(height,sizeof(height),0); for i:=1 to n do begin if rank[i]=1 then continue; k:=height[rank[i-1]]-1; if k<0 then k:=0; while (sa[rank[i]-1]+k<=n)and(i+k<=n)and(root[i+k]=root[sa[rank[i]-1]+k]) do inc(k); height[rank[i]]:=k; end; s:=1; e:=n; while s<>e do begin mid:=s+e-(s+e) div 2; min:=maxlongint div 10; max:=-maxlongint div 10; ok:=false; for i:=2 to n do if height[i]<mid then begin min:=maxlongint div 10; max:=-maxlongint div 10; end else begin if sa[i]<min then min:=sa[i]; if sa[i]>max then max:=sa[i]; if sa[i-1]<min then min:=sa[i-1]; if sa[i-1]>max then max:=sa[i-1]; if max-min>mid then begin ok:=true; break; end; end; if ok then s:=mid else e:=mid - 1; end; if s+1<5 then writeln(0) else writeln(s+1); until false; end.