字符串模板

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 20 ; 
struct SAM{
    int t[N][26] , link[N] , len[N];
    int last ,tot ; 
    SAM() : last(1) , tot(1){}
    void insert(int c){
        int p = last , np = last = ++tot ; //p是之前的节点 np是现新建的节点
        len[np] = len[p] + 1 ; //首先会多一个endpos为n的,其他的不可能和他重复
        while( p and !t[p][c] )t[p][c] = np , p = link[p];//link[p]相当于fail指针 向上跳使得s -> s的后缀 在后缀上->一个 就相当于是一个新的子串
        if(!p)link[np] = 1 ;//如果跳到根了,说明没有与他相同的子串,所以失配只能跳到根
        else{//如果有和他一样的子串
            int q = t[p][c] ; 
            if( len[q] == len[p] + 1)link[np] = q ;//如果说是s+c的等价后继类是s的等价类最长的+1 ,那么他就是等价后继类
            else{
                int nq = ++tot ; 
                memcpy( t[nq] , t[q] , sizeof(t[q]));//转移函数不变
                len[nq] = len[p] + 1 ; //新加了一个c所以长度+1
                link[nq] = link[q] ;//
                /*如图
                    nq
                   /  \
                  q    np 
                */
                link[np] = link[q] = nq ;//
                while( p and t[p][c] == q )t[p][c] = nq , p = link[p];//更新其他的到他的节点
            }
        }
    }
}
void get_sa(){
    int m = 255 ;
    for(int i = 1 ; i <= n ;++i)cnt[rnk[i] = s[i]]++; //初始化,给长度为1的赋值
    for(int i = 1 ; i <= m ;++i)cnt[i] += cnt[i - 1] ;//前缀和,计算
    for(int i = n ; i >= 1 ;--i)sa[cnt[rnk[i]]--] = i ; 
    for(int w = 1 , t = 0 ; ; m = t , t = 0 , w <<= 1 ){//每次倍增长度
        for(int i = n - w + 1 ; i <= n ;++i)tmp[++t] = i;//如果 i + w > 0 就把他平行移到左边
        for(int i = 1 ; i <= n ;++i)
            if( sa[i] > w )tmp[++t] = sa[i] - w ;//否则直接赋值
        // for(int i = 1 ; i <= n ; i++){
        //     cout << i << " " << tmp[i] << " " << w << endl ;  
        // } 
        // cout << endl ; 
        for(int i = 0 ; i <= m ;++i)cnt[i] = 0 ;//重置计数器
        for(int i = 1 ; i <= n ;++i)cnt[rnk[tmp[i]]]++;//对第一关键字进行计数排序
        for(int i = 1 ; i <= m ;++i)cnt[i] += cnt[i - 1];//前缀和
        for(int i = n ; i >= 1 ;--i)sa[cnt[rnk[tmp[i]]]--]= tmp[i];//计算sa  
        swap(rnk , tmp) , t = 0 ;//这里应该是节省空间 直接使用把老的rnk赋给tmp , 用老的rnk更新新的rnk
        for(int i = 1 ; i <= n ; i++)
            rnk[sa[i]] = (tmp[sa[i]] == tmp[sa[i - 1]] and tmp[sa[i] + w] == tmp[sa[i - 1] + w] ) ? t : ++t ; 
        if( t == n )break ;//如果已经够了就不再排序了
    }
}
void getHeight(){
    for(int i = 1 , k = 0 ; i <= n ; ++i){
        if(k)--k;
        while(s[i + k] == s[sa[rnk[i] - 1] + k])k++;//直接暴力扩展
        height[rnk[i]] = k ;
    }
}
posted @ 2025-07-28 20:25  Super_lollipop  阅读(23)  评论(1)    收藏  举报