SAM填坑告一段落了(爲什麼代碼格式變了,高亮也亂七八糟的)
https://paste.ubuntu.com/p/TTht5PVZww/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;//字符串長度
const int maxc=26;
char s[maxn];
struct Suffix_Automaton {//打*的不一定用到
int next[maxn<<1][maxc]; //狀態轉移(尾部加一個字符的下一個狀態)
int len[maxn<<1]; //最長子串的長度(該節點子串數量=len[x]-len[link[x]])
int link[maxn<<1]; //後綴鏈接(最短串前部減少一個字符所到達的狀態)
// int cnt[maxn<<1]; //被後綴連接的數(*)
int id; //結點編號
int last; //最後結點
int endpos[maxn<<1]; // endpos數(一類子串的數量)
int a[maxn];
int b[maxn<<1];
// int sum[maxn<<1];//該節點後面所形成的子串的總數
// int dp[maxn<<1];
// int ans[maxn<<1];
// ll d[maxn<<1]; //d[i]表示從狀態i出發,不同的子串的數目,即不同的路徑數
void init() { //初始化
for(int i=1; i<=id; i++){ //常規初始化
link[i] = len[i] = 0;
memset(next[i],0,sizeof(next[i]));
endpos[i]=0;
a[i]=0;
b[i]=0;
}
// for(int i=1;i<=id;i++) {//非常規初始化
// d[i]=0;
// }
last = id = 1; //1表示root起始點 空集
}
//SAM建圖
void add(int c) { //插入字符,爲字符ascll碼值
int x = ++id; //創建一個新結點x;
len[x] = len[last] + 1; // 長度等於最後一個結點+1
endpos[x] = 1; //接受結點子串除後綴連接還需加一
int p; //第一個有C轉移的結點;
for (p = last; p && !next[p][c]; p = link[p])
next[p][c] = x;//沿着後綴連接 將所有沒有字符c轉移的節點直接指向新結點
if (!p){ //全部都沒有c的轉移 直接將新結點後綴連接到起點
link[x] = 1;
// cnt[1]++;
}
else {
int q = next[p][c]; //p通過c轉移到的結點
if (len[p] + 1 == len[q]){//pq是連續的
link[x] = q;
// cnt[q]++; //將新結點後綴連接指向q即可,q結點的被後綴連接數+1
}
else {
int nq = ++id; //不連續 需要複製一份q結點
len[nq] = len[p] + 1; //令nq與p連續
link[nq] = link[q]; //因後面link[q]改變此處不加cnt
memcpy(next[nq], next[q], sizeof(next[q])); //複製q的信息給nq
for (; p&&next[p][c] == q; p = link[p])
next[p][c] = nq; //沿着後綴連接 將所有通過c轉移爲q的改爲nq
link[q] = link[x] = nq; //將x和q後綴連接改爲nq
//cnt[nq] += 2; // nq增加兩個後綴連接
}
}
last = x; //更新最後處理的結點
}
void getTP(int &Len){//對sam的節點按照len,從小到大排序重新標號,即給定節點的拓撲序
for(int i=1;i<=id;i++) a[len[i]]++;
for(int i=1;i<=Len;i++) a[i]+=a[i-1];
for(int i=1;i<=id;i++) b[a[len[i]]--]=i;
}
void getendpos(){//求每類子串的數量 ,即endpos集合的大小
for(int i=id;i>=1;i--){ //按拓撲序遍歷
int e=b[i];
endpos[link[e]]+=endpos[e];
}
}
/*
ll getSubNum() { //求不相同子串數量
ll ans = 0;
for (int i = 2; i <= id; i++)
ans += len[i]-len[link[i]]; //一狀態子串數量等於len[i]-len[link[i]]
return ans;
}
*/
/*
void get_len_max(int Len){//求長度爲i的出現次數最多的子串的出現次數
for(int i=1;i<=id;i++) dp[len[i]]=max(dp[len[i]],endpos[i]);
for(int i=Len-1;i>=1;i--) dp[i]=max(dp[i],dp[i+1]);
for(int i=1;i<=Len;i++) printf("%d\n",dp[i]);
}
*/
/*
void getsum(){
for(int i=id;i>=1;i--){
int &e=b[i];
sum[e]=1;
for(int j=0;j<26;j++){
sum[e]+=sum[next[e][j]];
}
}
}
void getmink(int k){ //求字典序第k小的子串
int now=1,p;
string s="";
while(k){
for(int i=0;i<26;i++){
if(next[now][i]&&k){
p=next[now][i];
if(sum[p]<k) k-=sum[p];
else{
k--;
now=p;
s+=(char)(i+'a');
break;
}
}
}
}
cout<<s<<endl;
}
*/
/*
void LCS1(char s[],int Len){//求兩個串的最長公共子串
int ans=0,cnt=0;
int now=1;
char base='a';
for(int i=0;i<Len;i++){
int c=s[i]-base;
if(next[now][c]){
cnt++;
now=next[now][c];
}
else{
while(now&&!next[now][c]) now=link[now];
if(!now) cnt=0,now=1;
else cnt=len[now]+1,now=next[now][c];
}
ans=max(ans,cnt);
}
printf("%d\n",ans);
}
*/
/*
void init_ans(){
for(int i=1;i<=id;i++) ans[i]=len[i];
}
void LCS2(char s[],int Len){ //求多個串的最長公共子串
for(int i=1;i<=id;i++) dp[i]=0;
int cnt=0;
int now=1;
char base='a';
for(int i=0;i<Len;i++){
int c=s[i]-base;
if(next[now][c]){
cnt++;
now=next[now][c];
}
else{
while(now&&!next[now][c]) now=link[now];
if(!now) cnt=0,now=1;
else cnt=len[now]+1,now=next[now][c];
}
dp[now]=max(dp[now],cnt);
}
for(int i=id;i>=1;i--){
int e=b[i];
dp[link[e]]=max(dp[link[e]],min(dp[e],len[link[e]]));
}
for(int i=1;i<=id;i++) ans[i]=min(ans[i],dp[i]);
}
void get_LCS2_ans(){
int cnt=0;
for(int i=1;i<=id;i++) cnt=max(cnt,ans[i]);
printf("%d\n",cnt);
}
*/
/*
void solve1(){ //求出現次數爲k的子串種數
ll ans=0;
for(int i=1;i<=id;i++){
if(endpos[i]==K){
ans+=len[i]-len[link[i]];
}
}
printf("%lld\n",ans);
}
*/
/*
void solve1(){//求出現次數A<=K<=B的子串種數
for(int i=id;i>1;i--){
int v=b[i];
if(endpos[v]>=A&&endpos[v]<=B) d[v]++;
for(int j=0;j<26;j++){
if(next[v][j]) d[v]+=d[next[v][j]];
}
}
ll ans=0;
for(int i=0;i<26;i++){
if(next[1][i]) ans+=d[next[1][i]];
}
printf("%lld\n",ans);
}
*/
/*
void solve2(){//求出現次數>=k的子串的最大長度
int ans=0;
for(int i=1;i<=id;i++){
if(endpos[i]>=K){
ans=max(ans,len[i]);
}
}
printf("%d\n",ans);
}
*/
} sam;
int main(){
scanf("%s",s);
int len=strlen(s);
sam.init();
for(int i=0;i<len;i++) sam.add(s[i]-'a');
sam.getTP(len);
sam.getendpos();
return 0;
}