poj3261

【题意】

给定一个长度为n的整数序列,求其中至少出现k次的子序列长度最长为多长

【输入】

第一行n和k

接下来n个数字描述序列

【输出】

一个数字,表示至少出现过k次的子序列最长长度


也是09年论文《后缀数组——处理字符串的有力工具》上的例题

不过不同之处是二分答案之时,判断方式是一组中是否有k个后缀


program poj3261;
var
  n,i,j,k,s,e,mid,min,max,less: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
  read(n,less);
  for i:=1 to n do
    read(root[i]);
  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;
      ok:=false;
      k:=0;
      for i:=2 to n do
        if height[i]>=mid then
          begin
            inc(k);
            if k>=less then
              begin
                ok:=true;
                break;
              end;
          end
                          else
          k:=1;
      if ok then s:=mid
            else e:=mid - 1;
    end;
  writeln(s);
end.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值