• 字符串板子


    宁就是板子集结大师? 人很难受,然后devinwang在讲烤馍片,然后开始copy代码。

    最小表示法

    gugugu

    字符串哈希

    子串哈希 O(1)

    (hash=hash_{r}-hash_{l-1}base^{r-l+1})

    证明:
    (hash(1,r)=sum_{i=1}^r sum a_ibase^{r-i})
    (hash(1,l-1)=sum_{i=1}^{l-1} sum a_ibase^{l-1-i})

    (hash(l,r)=sum_{i=l}^r sum a_ibase^{r-i})
    (hash(l,r)=sum_{i=1}^r sum a_ibase^{r-i}-sum_{i=1}^{l-1} sum a_ibase^{r-i})
    (hash(l,r)=hash(1,r)-sum_{i=1}^{l-1} sum a_ibase^{l-1-i}base^{r-l+1})
    (hash(l,r)=hash(1,r)-hash(1,l-1)base^{r-l+1})

    void pre(){
        p[0]=1;f[0]=0;
        for(int i=1;i<=n;i++){
            f[i]=(1ll*f[i-1]*bs+s[i]-'a')%mod;
            p[i]=1ll*p[i-1]*bs%mod;
        }
    }
    int calc(int l,int r){
        return ((f[r]-1ll*f[l-1]*p[r-l+1]%mod)%mod+mod)%mod;
    }
    

    KMP

    KMP-Matrix67

    模板-KMP

    输出在 (s1) 中所有出现的 (s2) 的位置

    定义一个字符串 (s) 的border 为一个非 (s) 的子串 (t) ,满足 (t) 既是 (s) 的前缀,又是 (s) 的后缀。

    输出 (s2) 的每个前缀 (s') 的最长border的长度。(实际上就是反悔操作)

    char s1[N],s2[N];
    int p[N],n,m;
    void pre(){
    	int j=0;p[1]=0;
    	for(int i=2;i<=n;i++) {
    		while(j>0&&s2[j+1]!=s2[i]) j=p[j];
    		if(s2[j+1]==s2[i]) j++;
    		p[i]=j;
    	}
    	return;
    }
    void kmp(){
    	int j=0;
    	for(int i=1;i<=n;i++) {
    		while(j>0&&s2[j+1]!=s1[i]) j=p[j];
    		if(s2[j+1]==s1[i]) j++;
    		if(j==m){
    			printf("%d
    ",i-m+1);
    			j=p[j];
    		}
    	}
    	return;
    }
    int main(){
    	scanf("%s",s1+1);scanf("%s",s2+1);
    	m=strlen(s2+1),n=strlen(s1+1);
    	pre();kmp();
    	for(int i=1;i<=m;i++)
    		printf("%d ",p[i]);
    	return 0;
    }
    

    字典树

    这就不写了吧草,这是一个01 trie的应用。

    void ins(ll x, int val){
        int p = 1;
        for(int i = 60; i >= 0; i--){
            sum[p] = (sum[p] + val) % mod;
            int c = ((x >> i) & 1);
            if(!ch[p][c]) ch[p][c] = ++sz;
            p = ch[p][c];
        }
        if(p) sum[p] = (sum[p] + val) % mod;
        return;
    }
    int query(ll x){
        int p = 1, ret = 0;
        for(int i = 60; i >= 0; i--){
            int c = ((x >> i) & 1);
            if((X >> i) & 1) p = ch[p][c ^ 1];
            else ret = (ret + sum[ch[p][c ^ 1]]) % mod, p = ch[p][c];
            if(!p) break;
        }
        if(p) ret = (ret + sum[p]) % mod;
        return ret;
    }
    

    AC自动机

    已经炸了的bestsort的博客链接

    和著名的某张图。注意e不是结束节点

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+10;
    int n, cnt=0, tr[N][30], tot[N], fail[N];
    char ch[N], t[N];
    void insert(char* s){
            int len = strlen(s+1);
            int p = 0;
            for(int i = 1; i <= len; i++){
                    int c=s[i] - 'a';
                    if(!tr[p][c])  tr[p][c] = ++cnt; 
                    p=tr[p][c];
            }
            tot[p]++;
    }
    void getfail(){
            queue<int>q;
            for(int c = 0; c < 26; c++)
                    if(tr[0][c]) fail[tr[0][c]] = 0, q.push(tr[0][c]);
            while(!q.empty()){
                    int p = q.front(); q.pop();
                    for(int c = 0; c < 26; c++){
                            if(tr[p][c]) fail[tr[p][c]] = tr[fail[p]][c], q.push(tr[p][c]);
                            else tr[p][c] = tr[fail[p]][c];
                    }
            }
    }
    int query(char* s){
            int p = 0, ret = 0, len = strlen(s+1);
            for(int i = 1; i <= len; i++){
                    int c = s[i] - 'a';
                    p = tr[p][c];
                    for(int j = p; j && tot[j] != -1; j = fail[j])
                            ret += tot[j], tot[j] = -1;
            }
            return ret;
    }
    int main(){
            scanf("%d", &n);
            for(int i = 1; i <= n; i++){
                    scanf("%s",ch+1);
                    insert(ch);
            }
            fail[0] = 0; getfail();
            scanf("%s",t + 1);
            printf("%d
    ", query(t));
            return 0;
    }
    

    Manacher

    KSkun大佬的blog

    Oh how confused this konjac is! It is tring to write down some notes to help itself!

    洛谷-【模板】manacher

    给出一个字符串 S ,求 S 中最长回文串的长度 。

    • 朴素算法是以每一个点和间隔为中点往外扫。时间复杂度 (O(n^2))

    • 马拉车算法 时间复杂度 (O(n))

    首先,构造一个字符串 (s2) ,将字符串 (s1) 的尾以及间隔处插入 #,头部插入$

    如果s1=aaa,则s2=$a#a#a#

    这样做的效果是把所有回文串对应到了一个奇回文串上,将对接下来的操作有帮助。

    设一个数组 (p_i) 表示第 (i) 个字符的回文半径。

    例如 $#a#b#c#b#a#a#b#c#b#a# 其中第一个c的回文串就是#a#b#c#b#a#

    回文半径就是6。

    如何用 (p_{1...i-1}) 推出 (p_i)

    (mx) 为当前最大回文串右边界, (id) 为当前最大回文串对称中心。

    (mx=id+p[id])

    我们先求出以 (i)为中心的回文半径至少有多长。

    • i<mx 时:

    (p_i) 的值可以通过 (p_j) 转移而来,因为左右是关于id对称的。也就是说,深蓝=浅蓝,深绿=浅绿。需要注意的是,(mx-i) 可能会 (<p_j) 此时 (p_i=mx-i)

    所以 (p_i=min(p_{2*id-i},mx-i))

    • i>mx 时,直接先设 (p_i=1)

    之后再往两遍扩展即可。求出 (p_i) 后要更新 (mx)(id)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=11e6+10;
    int n,p[N<<1];
    char s1[N],s2[N<<1];
    int manacher(){
        int tot=0;
        s2[tot++]='$',s2[tot++]='#';
        for(int i=1;i<=n;i++)
            s2[tot++]=s1[i],s2[tot++]='#';
        tot--;
        int mxlen=0,mx=0,id=0;
        for(int i=1;i<=tot;i++){
            if(i<mx) p[i]=min(p[id*2-i],mx-i);
            else p[i]=1;
            while(s2[i-p[i]]==s2[i+p[i]]) p[i]++;
    
            if(mx<i+p[i]) mx=i+p[i],id=i;
            mxlen=max(mxlen,p[i]-1);
        }
        return mxlen;
    }
    int main(){
        scanf("%s",s1+1);
        n=strlen(s1+1);
        printf("%d
    ",manacher());
        return 0;
    }
    

    SAM

    太长了

    写的乱七八糟,待填坑

    qaqaq
  • 相关阅读:
    Markdown 画 UML 图(六)
    Markdown 高级技巧(五)
    Markdown 链接、图片、表格(四)
    16.3Sum Closet
    15.Three Sum
    11.Container With Most Water
    1.Two Sum
    优化学习笔记5
    优化学习笔记4
    优化学习笔记3
  • 原文地址:https://www.cnblogs.com/zdsrs060330/p/14745360.html
Copyright © 2020-2023  润新知