其他分享
首页 > 其他分享> > [bzoj1396]识别子串

[bzoj1396]识别子串

作者:互联网

题面:

看起来有点吓人实则不然

用SA或是SAM都可以过

SAM做法:

(哪天有空再补)

SA做法:

求完SA之后,对于每一个后缀$i$

以这个后缀为起点的最短唯一子串长度应该是$max(hei[i],hei[i+1])+1$

之后用线段树覆盖对应区间就行

⑨:Baka Baka~

当然如果一个后缀为起点没有唯一子串的要特判一下

对于这样的后缀就不进行线段树覆盖

⑩:是~这样吗~

到这里时我自己突然想到不对了

假设一个后缀的最短唯一子串为$s(i,j)$,那么在$j$后面的字符怎么办?

因为这个最短唯一子串对它后面的每一位$k$也有着$k-i+1$的贡献

处理方法

ans[i]=min(ans[i],ans[i-1]+1)

本题完结

  1 #include<cstdio>
  2 #include<cstring>
  3 const int N=100011;
  4 char s[N];
  5 int n;
  6 
  7 int max(int a,int b){return a>b?a:b;}
  8 int min(int a,int b){if(!a) return b;if(!b) return a;return a<b?a:b;}
  9 
 10 int sa[N],rank[N],hei[N];
 11 int buk[N],lst[N];
 12 bool cmp(int x,int y,int k)
 13 {
 14     if(x+k>n||y+k>n) return 0;
 15     return rank[x]==rank[y]&&rank[x+k]==rank[y+k];
 16 }
 17 void getsa()
 18 {
 19     int cnt=0;
 20     for(int i=1;i<=n;i++) buk[s[i]]++;
 21     for(int i=1;i<128;i++) if(buk[i]) lst[i]=++cnt;
 22     for(int i=1;i<128;i++) buk[i]+=buk[i-1];
 23     for(int i=1;i<=n;i++)
 24     {
 25         rank[i]=lst[s[i]];
 26         sa[buk[s[i]]--]=i;
 27     }
 28     for(int k=1;cnt!=n;k<<=1)
 29     {
 30         for(int i=1;i<=n;i++) buk[i]=0;
 31         for(int i=1;i<=n;i++) buk[rank[i]]++;
 32         for(int i=1;i<=n;i++) buk[i]+=buk[i-1];
 33         for(int i=n;i;i--)
 34             if(sa[i]>k) lst[sa[i]-k]=buk[rank[sa[i]-k]]--;
 35         for(int i=1;i<=k;i++)
 36             lst[n-i+1]=buk[rank[n-i+1]]--;
 37         for(int i=1;i<=n;i++) sa[lst[i]]=i;
 38         cnt=0;
 39         for(int i=1;i<=n;i++) lst[sa[i]]=cmp(sa[i],sa[i-1],k)?cnt:++cnt;
 40         for(int i=1;i<=n;i++) rank[i]=lst[i];
 41     }
 42 }
 43 void gethei()
 44 {
 45     for(int i=1;i<=n;i++)
 46     {
 47         if(rank[i]==1) continue;
 48         for(int j=max(1,hei[rank[i-1]]-1);;j++)
 49         {
 50             if(s[i+j-1]==s[sa[rank[i]-1]+j-1]) hei[rank[i]]=j;
 51             else break;
 52         }
 53     }
 54 }
 55 
 56 struct mokou
 57 {
 58     int v[N<<2];
 59     void fuckdown(int px)
 60     {
 61         v[px<<1]=min(v[px<<1],v[px]);
 62         v[px<<1|1]=min(v[px<<1|1],v[px]);
 63         return;
 64     }
 65     void cover(int l,int r,int c,int px,int pl,int pr)
 66     {
 67         if(l<=pl&&r>=pr)
 68         {
 69             v[px]=min(v[px],c);
 70             return;
 71         }
 72         fuckdown(px);
 73         int mid=(pl+pr)>>1;
 74         if(l<=mid) cover(l,r,c,px<<1,pl,mid);
 75         if(r>mid) cover(l,r,c,px<<1|1,mid+1,pr);
 76         return;
 77     }
 78     int query(int x,int px,int pl,int pr)
 79     {
 80         if(pl==x&&pr==x) return v[px];
 81         fuckdown(px);
 82         int mid=(pl+pr)>>1;
 83         if(x<=mid) return query(x,px<<1,pl,mid);
 84         else return query(x,px<<1|1,mid+1,pr);
 85     }
 86 }tr;
 87 int ans[N];
 88 
 89 int main()
 90 {
 91     scanf("%s",s+1);
 92     n=strlen(s+1);
 93     getsa();
 94     gethei();
 95     for(int i=1;i<=n;i++)
 96     {
 97         if(sa[i]+max(hei[i],hei[i+1])>n) continue;
 98 
 99         tr.cover(sa[i],sa[i]+max(hei[i],hei[i+1]),max(hei[i],hei[i+1])+1,1,1,n);
100     }
101     for(int i=1;i<=n;i++)
102         ans[i]=tr.query(i,1,1,n);
103     for(int i=2;i<=n;i++) ans[i]=min(ans[i],ans[i-1]+1);
104     for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
105     return 0;
106 }
Orz

 

*神的上升*

是我,水题男孩

我做后缀数组水题,好吗

不!

搜大友

*愤怒*

*激烈的对视大赛*

你够了,我无法忍受你的行为

现在你将成为我们中的议员(指做有思维含量的题)

*水题男孩上升到了新的境界*

 

标签:子串,return,int,rank,后缀,识别,hei,bzoj1396
来源: https://www.cnblogs.com/rikurika/p/bzoj1396.html