字符串模板(后缀数组、后缀自动机、回文树)

再整理一遍板子

后缀数组

例题cf432D:问对字符串s,对于所有的前缀,当它等于同长度后缀时,这个子串一共在s中出现多少次。

后缀数组求lcp是logn,显然直接二分即可。复杂度nlogn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+2;
int n,sa[N],rnk[N],height[N];
char s[N];
int st[N][20];
//sa数组表示字典序为i的后缀是谁,rnk数组表示某个后缀的排名(1~n)
void buildSA(int m=128){
    int cnt[N],rnk1[N],rnk2[N],tmpSA[N];    //cnt[i]用来记录rnk小于等于i的子串已经有多少个了,这样可以直接用cnt[rnk[i]]更新sa
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;++i)
        cnt[(int)s[i]]++;
    for(int i=1;i<=m;i++)
        cnt[i]+=cnt[i-1];
    for(int i=1;i<=n;i++)
        rnk[i]=cnt[(int)s[i]];

    for(int l=1;l<n;l<<=1){
        for(int i=1;i<=n;++i){
            rnk1[i]=rnk[i];
            rnk2[i]=(i+l<=n?rnk[i+l]:0); //如有缺失,名次按0算,即较短的占优势,它和较长的比较时缺失部分的优先级按最高算
        }
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;++i)
            cnt[rnk2[i]]++;
        for(int i=1;i<=n;++i)    //小于等于当前rnk的共有几个后缀
            cnt[i]+=cnt[i-1];
        for(int i=n;i;--i)         //tmpSA这里按rnk2名次记录的后缀
            tmpSA[cnt[rnk2[i]]--]=i;    //--是为了区分rnk相同的串,后访问的排序靠前一些
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;++i)
            cnt[rnk1[i]]++;
        for(int i=1;i<=n;++i)
            cnt[i]+=cnt[i-1];
        for(int i=n;i;--i)
            sa[cnt[rnk1[tmpSA[i]]]--]=tmpSA[i];    //是按照tmpSA的逆序计算,也就是rnk2较大的对应的cnt值会大一些,排在rnk1相同而rnk2较小的后面
        //cnt代表每个桶的大小
        bool uniq=true;     //如果构建完rnk数组还是true,说明所有后缀的大小已经区分出来了,没有排名相同的后缀了,可以退出
        rnk[sa[1]]=1;       //构建新的rank数组
        for(int i=2;i<=n;++i){
            rnk[sa[i]]=rnk[sa[i-1]];
            if(rnk1[sa[i]]==rnk1[sa[i-1]]&&rnk2[sa[i]]==rnk2[sa[i-1]])
                uniq=false;     //这里不能break,因为还要继续把当前rank数组算完
            else
                rnk[sa[i]]++;
        }
        if(uniq)
            break;
    }
}
//height[i]表示位于sa[i-1]和sa[i]的两个后缀的最长前缀
//有性质height[rnk[i]]>=height[rnk[i-1]]-1,即字符串向后推一位,height值最多减小1
void getHeight() {
    for(int i=1,k=0;i<=n;++i){
        if(k) --k;        //新的长度不会比k-1小,是结论
        int j=sa[rnk[i]-1]; //j是排序刚好比i小1的后缀
        while(s[i+k]==s[j+k])
            k++;
        height[rnk[i]]=k;
    }
}

//满足lcp(l,r)=min{height[i+1],...height[r]},故是RMQ问题
//st[i][j]表示[i,i+(1<<j)-1]的最小height,[i,i+(1<<j)]的lcp,这一点注意一下
void initST(){    //用ST表进行RMQ
    for(int i=1;i<n;i++)
        st[i][0]=height[i+1];
    for(int l=1;(1<<l)<=n-1;l++){
        for(int i=1;i+(1<<l)-1<=n;i++)
            st[i][l]=min(st[i][l-1],st[i+(1<<(l-1))][l-1]);
    }
}

//lcp:最长公共前缀,此处指sa[l]和sa[r]的最长公共前缀
int lcp(int l,int r){
    if(l==r)
        return n-sa[l]+1;
    if(l>r)
        swap(l,r);
    int k=0;
    while((1<<(k+1))+1<=r-l+1) ++k;
    return min(st[l][k],st[r-(1<<k)][k]);
}
vector<pair<int,int>> ans;
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    buildSA();
    getHeight();
    initST();
    for(int len=1;len<=n;len++){
        int a=rnk[1],b=rnk[n-len+1];
        if(lcp(a,b)<len)
            continue;
        if(a>b) swap(a,b);
        int res=b-a+1;
        int l=1,r=a;
        while(l<=r){
            int mid=(l+r)/2;
            if(lcp(mid,a)>=len)
                r=mid-1;
            else
                l=mid+1;
        }
        res+=max(a-l,0);
        l=b,r=n;
        while(l<=r){
            int mid=(l+r)/2;
            if(lcp(mid,b)>=len)
                l=mid+1;
            else
                r=mid-1;
        }
        res+=max(r-b,0);
        ans.push_back(make_pair(len,res));
        //printf("%d %d\n",len,res);
    }
    cout<<ans.size()<<endl;
    for(auto p:ans)
        cout<<p.first<<' '<<p.second<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此微信小程序医院挂号预约系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。微信小程序医院挂号预约系统有管理员,用户两个角色。管理员功能有个人中心,用户管理,医生信息管理,医院信息管理,科室信息管理,预约信息管理,预约取消管理,留言板,系统管理。微信小程序用户可以注册登录,查看医院信息,查看医生信息,查看公告资讯,在科室信息里面进行预约,也可以取消预约。微信小程序医院挂号预约系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absoler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值