@xzyxzy
2018-07-02T10:15:58.000000Z
字数 4717
阅读 1859
字符串
一个可以对一个字符串的所有后缀处理的算法
网上优秀博客
[luogu3809]【模板】后缀排序
板子,求数组
[hihocoder1403] 后缀数组一·重复旋律
求后单调队列求解至少出现次的最长子串
[hihocoder1407] 后缀数组二·重复旋律2
[hihocoder1415] 后缀数组三·重复旋律3
[POJ2774]Long Long Message
[Luogu2852][USACO06DEC]牛奶模式Milk Patterns
求两个字符串中的最长公共子串,把两串中间用一个从未出现过的字符连起来,跑后二分答案,如果有互不重叠且之大于的那么返回
[BZOJ3238][AHOI2013]差异Hard
求串中任意两个子串的(最长公共前缀),利用性质,的为,所以维护一个单调递增栈,设表示栈中第个位置到个位置的所有贡献,通过可以算贡献
[Luogu2463][SDOI2008]Sandy的卡片
[BZOJ2946]公共串
求多串中的最长公共子串,将所有串连接成一个串,中间用一个从未出现过的字符隔开,那么后二分最长公共子串的长度,若数组中有连续的一段,且这一段中所有的子串都出现过,那么合法
[BZOJ1031]字符加密
把字符串,在按照数组输出符合条件的字符
[BZOJ4199][NOI2015]品酒大会Hard
求最长公共前缀长度为的后缀对数以及得分最大的一对的得分。
那么一段区间都大于等于,它会被都计算贡献,所以大的贡献不会影响小的贡献,把height排序之后从大到小处理,每处理一个就把两个串并起来,表示之后会一起处理,同时答案加上两个并查集的之积(两并查集内任意一对都符合条件),最大值便是两并查集最大值之积或最小值之积(负数)
[BZOJ4556][TJOI2016&&HEOI2016]字符串Hard
求一个串与一段区间内的串的最长前缀。
二分答案,那么符合答案的是上一段连续的区间,分别二分左右端点,现在就是判断的上是否存在一个数在之间,转化为二维数点问题,用主席树完成
[luogu4143]采集矿石Hard
得到函数单调的性质后,考虑用后缀数组求一个子串的排名
其实一个后缀贡献的本质不同的字符串就是
1如果
2其他情况找到一个最大的使得那么套用上式
多个字符不好处理的时候在中间依次添加从未出现过的字符(第一次加第二次加什么的),使得前后两个字符不会有的贡献
排好序后,二分一段区间使得以区间任意一个位置为开头的字符串与字符串(也在区间内)的最长前缀时,建议分别二分左右端点,而不是倍增地跳(调试2hTJOI2016字符串,因为不好选取倍增起点,ST表中任意一个数值都至少由两个字符串得来)
洛谷3809后缀排序
记忆方式
Sort里面4行,预处理1行,倍增求SA枚举1行,中间5行
求height2行枚举3行内容
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>using namespace std;const int MAXN=2200000;char ch[MAXN];//字符串int m=1000;//字符集大小int s[MAXN],l,len1,len2;//字符串长度int SA[MAXN];//SA[i]表示排名为i的字符串是以SA[i]为开头的后缀int x[MAXN],y[MAXN];//记录两维信息方便排序int t[MAXN];//桶int Rank[MAXN];int height[MAXN];/*SA[i]=k 表示排名为i的是第k号串x[i]=k 表示i号串的第一关键字排名为ky[i]=k 表示第二关键字排名为i的是第k号串t[i]=k (统计前缀和之前)表示排名并列为i的串的数量是k(统计前缀和之后)表示排名为1~i的串的数量是kRank[i]=k 表示第i号串的排名是k(与SA互逆)height[i]=k 表示lcp(Suffix(SA[i-1]),Suffix(SA[i]))就是排名为i-1的串和排名为i的串的最长公共前缀*/int cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}void Sort(){for(int i=1;i<=m;i++) t[i]=0;for(int i=1;i<=l;i++) t[x[i]]++;for(int i=1;i<=m;i++) t[i]+=t[i-1];for(int i=l;i>=1;i--) SA[t[x[y[i]]]--]=y[i];//容易打反}void Get_SA(){for(int i=1;i<=l;i++) x[i]=s[i],y[i]=i; Sort();for(int k=1,p=0;k<=l;k<<=1){for(int i=l-k+1;i<=l;i++) y[++p]=i;for(int i=1;i<=l;i++) if(SA[i]>k) y[++p]=SA[i]-k;Sort();swap(x,y);x[SA[1]]=p=1;for(int i=2;i<=l;i++) x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;//容易x中不写SA[i]if(p==l) break; m=p;p=0;//如果第一维已经有序,那么就不需要继续排序了~}/*for(int i=1;i<=l;i++) Rank[SA[i]]=i;for(int i=1,j=0;i<=l;i++){if(j) j--; //利用性质:height[Rank[i]]>=height[Rank[i-1]]-1while(s[i+j]==s[SA[Rank[i]-1]+j]) j++;height[Rank[i]]=j;}*/}int main(){scanf("%s",ch); int len=strlen(ch);for(int i=0;i<len;i++) s[++l]=ch[i];Get_SA();for(int i=1;i<=l;i++) printf("%d ",SA[i]);puts("");return 0;}