数据结构 —— 字符串:后缀数组_Jetiaime的博客-CSDN博客(算法代码)
后缀数组_KonjakLAF的博客-CSDN博客(应用+例题)
- #include
- using namespace std;
- typedef long long ll;
- const int N=1e7+5;
- const int inf=1<<30;
- const ll INF=0x3f3f3f3f3f3f3f3f;
- int n,k;
- char s[N];
- int m,sa[N],cnt[N],rk[N],id[N],px[N],oldrk[N];
-
- inline bool cmp(int x,int y,int k){
- return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];
- }
- inline void getsa(){
- //初始化
- m=10;
- for(int i=1;i<=n;i++)cnt[rk[i]=s[i]-'0']++;
- for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
- //for(int i=1;i<=9;i++)printf("cnt[%d]:%d\n",i,cnt[i]);
- for(int i=n;i;i--)sa[cnt[rk[i]]--]=i;
- //for(int i=1;i<=n;i++)printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- //倍增
- for(int k=1;k<=n;k<<=1){//后缀长度
- int idx=0;
- for(int i=n;n-i
- for(int i=1;i<=n;i++)if(sa[i]>k)id[++idx]=sa[i]-k;//按后一半排序的后缀
- memset(cnt,0,sizeof(cnt));
- for(int i=1;i<=n;i++)cnt[px[i]=rk[id[i]]]++;
- for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
- for(int i=n;i;i--)sa[cnt[px[i]]--]=id[i];
- idx=0;swap(oldrk,rk);
- for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i],sa[i-1],k)?idx:++idx;
- m=idx;
- if(n==m)break;
- //for(int i=1;i<=n;i++)printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- }//for(int i=1;i<=n;i++)printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- }
- int val[N],len;
- inline void add(ll x){
- ll pre=x;
- for(int i=1;i<=len;i++){
- if(!pre)break;
- val[i]+=pre;
- pre=val[i]/10;
- val[i]%=10;
- }while(pre)val[++len]=pre%10,pre/=10;
- }
- int main(){
- scanf("%d%d",&n,&k);
- scanf("%s",s+1);
- getsa();
- ll res=0;len=n-k;
- for(int i=n;i;i--){
- if(sa[i]<=k+1){//sa[i]--sa[i]+n-k
- //printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- for(int j=sa[i],anj=len;j<=sa[i]+n-k-1;j++,anj--){
- //printf("s[%d]:%c %d\n",j,s[j],s[j]-'0');
- //res=res*10+int(s[j]-'0');
- val[anj]=s[j]-'0';
- }
- for(int j=1;j
'0'; - for(int j=sa[i]+n-k;j<=n;j++)res+=s[j]-'0';
- //printf("%lld",res);
- add(res);
- for(int i=len;i;i--)printf("%d",val[i]);
- break;
- }
- }
- return 0;
- }
例题:
例1:P3809 【模板】后缀排序 - 洛谷 (luogu.com.cn)
- #include
- using namespace std;
- typedef long long ll;
- const int N=1e6+5;
- const int inf=1<<30;
- const ll INF=0x3f3f3f3f3f3f3f3f;
- char s[N];
- int l1,l2,n;
- int idx,m,sa[N],rk[N],oldrk[N],px[N],id[N],c[N];
- bool cmp(int x,int y,int k){
- return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];
- }
- void getsa(){
- //初始化
- m=150;
- for(int i=1;i<=n;i++)c[rk[i]=s[i]]++;
- for(int i=1;i<=m;i++)c[i]+=c[i-1];
- //for(int i=1;i<=m;i++)printf("c[%d]%d\n",i,c[i]);
- for(int i=n;i;i--)sa[c[rk[i]]--]=i;
- //for(int i=1;i<=n;i++)printf("sa[%d]%d %s\n",i,sa[i],s+sa[i]);
- //倍增
- for(int k=1;k<=n;k<<=1){
- idx=0;
- for(int i=n;n-i
- for(int i=1;i<=n;i++)if(sa[i]>k)id[++idx]=sa[i]-k;
- memset(c,0,sizeof(c));
- for(int i=1;i<=n;i++)c[px[i]=rk[id[i]]]++;
- for(int i=1;i<=m;i++)c[i]+=c[i-1];
- for(int i=n;i;i--)sa[c[px[i]]--]=id[i];
- swap(rk,oldrk);
- idx=0;
- for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i],sa[i-1],k)?idx:++idx;
- if(idx==n)break;
- m=idx;
- //for(int i=1;i<=n;i++)printf("sa[%d]%d %s\n",i,sa[i],s+sa[i]);
- }
- }
- int ht[N];
- void geth(){
- for(int i=1,k=0;i<=n;i++){
- if(k)k--;
- while(s[i+k]==s[sa[rk[i]-1]+k])k++;
- ht[rk[i]]=k;
- }
- }
-
- inline void read(int &x){
- int f=1;
- x=0;
- char ch=getchar();
- while(ch<'0'||'9'
- if(ch=='-')f=-1;
- ch=getchar();
- }while('0'<=ch&&ch<='9'){
- x=(x<<3)+(x<<1)+(ch&15);
- ch=getchar();
- }x*=f;
- }
- inline void write(int x){
- if(x<0){
- putchar('-');x=-x;
- }if(x>9)write(x/10);
- putchar('0'+x%10);
- }
- int main(){
- scanf("%s",s+1);
- n=strlen(s+1);
-
- getsa();geth();
- for(int i=1;i<=n;i++)printf("%d ",sa[i]);
-
- return 0;
- }
例2:数字串 -matiji
题解:找到最大的n-k位数
eg. 6 2 121312
- 样例:
- 6 2
- 121312
- 排序后:
- 12
- 121312
- 1312
- 2
- 21312
- 312
倒序遍历找到最大的n-k位数
注意:*1.由于字符串长度最大1e6,会爆ll,所以存储每一位的数字然后输出
*2.getsa()函数中没有 if(n==m)break; 会超时
- #include
- using namespace std;
- typedef long long ll;
- const int N=1e7+5;
- const int inf=1<<30;
- const ll INF=0x3f3f3f3f3f3f3f3f;
- int n,k;
- char s[N];
- int m,sa[N],cnt[N],rk[N],id[N],px[N],oldrk[N];
-
- inline bool cmp(int x,int y,int k){
- return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];
- }
- inline void getsa(){
- //初始化
- m=10;
- for(int i=1;i<=n;i++)cnt[rk[i]=s[i]-'0']++;
- for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
- //for(int i=1;i<=9;i++)printf("cnt[%d]:%d\n",i,cnt[i]);
- for(int i=n;i;i--)sa[cnt[rk[i]]--]=i;
- //for(int i=1;i<=n;i++)printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- //倍增
- for(int k=1;k<=n;k<<=1){//后缀长度
- int idx=0;
- for(int i=n;n-i
- for(int i=1;i<=n;i++)if(sa[i]>k)id[++idx]=sa[i]-k;//按后一半排序的后缀
- memset(cnt,0,sizeof(cnt));
- for(int i=1;i<=n;i++)cnt[px[i]=rk[id[i]]]++;
- for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
- for(int i=n;i;i--)sa[cnt[px[i]]--]=id[i];
- idx=0;swap(oldrk,rk);
- for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i],sa[i-1],k)?idx:++idx;
- m=idx;
- if(n==m)break;
- //for(int i=1;i<=n;i++)printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- }//for(int i=1;i<=n;i++)printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- }
- int val[N],len;
- inline void add(ll x){
- ll pre=x;
- for(int i=1;i<=len;i++){
- if(!pre)break;
- val[i]+=pre;
- pre=val[i]/10;
- val[i]%=10;
- }while(pre)val[++len]=pre%10,pre/=10;
- }
- int main(){
- scanf("%d%d",&n,&k);
- scanf("%s",s+1);
- getsa();
- ll res=0;len=n-k;
- for(int i=n;i;i--){
- if(sa[i]<=k+1){//sa[i]--sa[i]+n-k
- //printf("sa[%d]:%d %s\n",i,sa[i],s+sa[i]);
- for(int j=sa[i],anj=len;j<=sa[i]+n-k-1;j++,anj--){
- //printf("s[%d]:%c %d\n",j,s[j],s[j]-'0');
- //res=res*10+int(s[j]-'0');
- val[anj]=s[j]-'0';
- }
- for(int j=1;j
'0'; - for(int j=sa[i]+n-k;j<=n;j++)res+=s[j]-'0';
- //printf("%lld",res);
- add(res);
- for(int i=len;i;i--)printf("%d",val[i]);
- break;
- }
- }
- return 0;
- }
例3:乐曲主题 - 洛谷 (最长不重叠公共子串)
转调后仍相同则差值相同,此时只需找不重叠的最长公共子串
可转化为不同后缀的最长公共前缀,且不同后缀的公共前缀不重叠
后缀数组计算时cnt[a[i]]计数,而差值a[i]可能为负数如 1-88,在此给每一个a[i]+90
有子串长度为 4 时 "主题" 长度为 5
二分答案,答案变成判定性问题。
将 height [ i ]分为连续的组,如果有连续的一段 height≥mid ,且 max(sai)−min(sai)>mid
说明存在长度为 mid且不重叠的重复子串。
*对 height [ i ] 分组的方法很重要。
- #include
- using namespace std;
- typedef long long ll;
- const int N=5e3+5;
- const int inf=1<<30;
- const ll INF=0x3f3f3f3f3f3f3f3f;
-
- inline void read(ll &x){
- ll f=1;
- x=0;
- char ch=getchar();
- while(ch<'0'||'9'
- if(ch=='-')f=-1;
- ch=getchar();
- }while('0'<=ch&&ch<='9'){
- x=(x<<3)+(x<<1)+(ch&15);
- ch=getchar();
- }x*=f;
- }
- inline void write(int x){
- if(x<0){
- putchar('-');x=-x;
- }if(x>9)write(x/10);
- putchar('0'+x%10);
- }
- int n,a[N];
- int m,sa[N],rk[N],oldrk[N],px[N],id[N],idx,ht[N],c[N];
- bool cmp(int x,int y,int k){
- return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];
- }
- void getsa(){
- //初始化
- m=180;
- for(int i=1;i<=n;i++)c[rk[i]=a[i]]++;
- for(int i=2;i<=m;i++)c[i]+=c[i-1];
- for(int i=n;i;i--)sa[c[rk[i]]--]=i;
- //倍增
- for(int k=1;k<=n;k<<=1){
- idx=0;
- for(int i=n;n-i
- for(int i=1;i<=n;i++)if(sa[i]>k)id[++idx]=sa[i]-k;
- memset(c,0,sizeof(c));
- for(int i=1;i<=n;i++)c[px[i]=rk[id[i]]]++;
- for(int i=2;i<=m;i++)c[i]+=c[i-1];
- for(int i=n;i;i--)sa[c[px[i]]--]=id[i];
- //更新rk
- swap(rk,oldrk);idx=0;
- for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i],sa[i-1],k)?idx:++idx;
- if(idx==n)break;
- m=idx;
- }
- }
- void geth(){
- for(int i=1,k=0;i<=n;i++){
- if(k)k--;
- while(a[i+k]==a[sa[rk[i]-1]+k])k++;
- ht[rk[i]]=k;
- }
- }
- int chk(int x){
- int mn=sa[1],mx=sa[1];
- for(int i=2;i<=n;i++){
- if(ht[i]
- mn=mx=sa[i];
- }else {
- mn=min(mn,sa[i]);
- mx=max(mx,sa[i]);
- if(mx-mn>x)return 1;
- }
- }return 0;
- }
- /*
- a 0
- aabcdd 1
- aabckm 4
- aabcko 5
- bac 0
- 此时ht为1 4 5的是一组 sa[i]max-sa[i]min+1 >= mid则不重复
- */
- int main(){
- scanf("%d",&n);
- for(int i=1;i<=n;i++)scanf("%d",&a[i]);
- for(int i=1;i
1]-a[i]+90; - a[n--]=0;
- /*
- 转调后仍相同则差值相同,此时只需找不重叠的最长公共子串
- 可转化为不同后缀的最长公共前缀,且不同后缀的公共前缀不重叠
- 后缀数组计算时cnt[a[i]]计数,而差值a[i]可能为负数如 1-88,在此给每一个a[i]+90
- 有子串长度为 4 时 "主题" 长度为 5
- */
- getsa();geth();
- /* 二分查找最长长度 -> 转化为判定问题 */
- //for(int i=1;i<=n;i++)printf("a[%d]%d\n",i,a[i]);printf("\n");
-
- //for(int i=1;i
- int l=0,r=n,res=0;
- while(l<=r){
- int mid=(l+r)/2;
- if(chk(mid))res=mid,l=mid+1;
- else r=mid-1;//printf("l%d r%d res%d\n",l,r,res);
- }
- printf("%d",res>=4?res+1:0);
- return 0;
- }
-
相关阅读:
arc 166 a
软件测试/测试开发丨Python安装指南(macOS)
BUG:阿里巴巴图标库引入链接后,icon有时候会不显示的话svg下载到本地使用
yarn安装及使用
一.node的文件系统;二.node的数据流(Stream接口);
华为OD机试真题【篮球比赛】
在Vue中使用Immutable.js
计算机毕业设计ssm+vue基本微信小程序的心理服务平台
z—libirary最新地址获取,zlibirary地址获取方式,zliabary最新地址,zliabary官网登录方式,zliabary最新登陆
Java编码与解码
-
原文地址:https://blog.csdn.net/m0_63851154/article/details/133087866