zoukankan      html  css  js  c++  java
  • 字符串板子

    宁就是板子集结大师? 人很难受,然后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
  • 相关阅读:
    Java 实现 蓝桥杯 生兔子问题
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 LeetCode 33 搜索旋转排序数组
    Java实现 LeetCode 33 搜索旋转排序数组
    Java实现 LeetCode 33 搜索旋转排序数组
    深入探究VC —— 资源编译器rc.exe(3)
    深入探究VC —— 编译器cl.exe(2)
    深入探究VC —— 编译器cl.exe(1)
  • 原文地址:https://www.cnblogs.com/zdsrs060330/p/14745360.html
Copyright © 2011-2022 走看看