字符串练习

快要区域赛了,恶补ing。
先复习一下学习过的字符串知识吧。
由于本人是弱渣,先来几个板子题磨练一下板子。

先来复习一下模式匹配的知识,写两个ac自动机。

HDU2222 Keywords Search
给多个模板串,进行匹配。
模板题。沿着trie树走,然后要沿着fail边跳,来统计。

POJ 2776
给一些禁止的串,然后问长度为n的没有禁止的串的串的个数。
在禁止的串构成的ac自动机上游走,然后可以转移,研究研究发现可以用矩阵加速。

板子新鲜出炉。

struct Ac{
    int ch[maxn * 50][26];
    int fail[maxn * 50];
    int val[maxn * 50];
    int q[maxn * 50];
    int tot;
    Ac(){}
    int id(char c){
        return c - 'a';
    }
    void init(){
        tot = 0;
        fail[0] = -1;memset(ch,0,sizeof(ch));val[0] = 0;
    }
    void insert(char *s,int len){
        int now = 0;
        for(int i = 0;i < len;i++){
            int tr = id(s[i]);
            if(ch[now][tr] == 0){
                ch[now][tr] = ++tot;
                memset(ch[tot],0,sizeof(ch[tot]));
                val[tot] = 0;
                fail[tot] = 0;
            }
            now = ch[now][tr];
        }
        val[now]++;
    }

    void make(){
        int h = 1,t = 0;
        for(int i = 0;i < 26;i++) if(ch[0][i] != 0) q[++t] = ch[0][i];
        while(h <= t){
            int now = q[h++];

            for(int i = 0;i < 26;i++){
                if(ch[now][i] == 0){
                    ch[now][i] = ch[fail[now]][i];
                }else{
                    fail[ch[now][i]] = ch[fail[now]][i];
                    q[++t] = ch[now][i];
                }
            }
        }
    }
    int search(char *s,int len){
        //不可思议的操作
    }

}ac;

后缀自动机

SPOJ694
求一个串本质不同的子串个数。sa和sam都可以做。先用sam来a一下。sdoi2016的生成魔咒还复杂一点,但都是模板题。

我的板子。

struct Sam{
    int fail[maxn * 2];
    int ch[maxn * 2][256];int tot,last;
    int mx[maxn * 2];
    int ans;
    void clear(int i){
        memset(ch[i],0,sizeof(ch[i]));
    }
    void clear(){
        tot = last = 1;
        ans = 0;
        clear(0);clear(1);
    }
    Sam(){
        tot = last = 1;
        ans = 0;
        clear(0);clear(1);
    }
    int cal(int x){
        return mx[x] - mx[fail[x]];
    }
    void add(int c){
        int p = last,np = last = ++tot;clear(tot);
        mx[np] = mx[p] + 1;
        while(p && ch[p][c] == 0) ch[p][c] = np,p = fail[p];
        if(!p) fail[np] = 1,ans += cal(np);
        else{
            int q = ch[p][c];
            if(mx[q] == mx[p] + 1) fail[np] = q,ans += cal(np);
            else{
                int nq = ++tot;clear(tot);mx[nq] = mx[p] + 1;
                memcpy(ch[nq],ch[q],sizeof(ch[nq]));
                fail[nq] = fail[q];ans+=cal(nq)-cal(q);
                fail[q] = fail[np] = nq;ans+=cal(np)+cal(q);
                while(p && ch[p][c] == q) ch[p][c] = nq,p = fail[p];
            }
        }
    }
}sam;

扩展kmp

普通的kmp是计算以i为结尾的串和前缀的最大重叠,这个是计算以i为开头的,理解了一下,感觉原理差不大多,都借用了一些前面的状态。然后细节稍微复杂一点。

抄了个板子。

//字符串以0开始
void get_next()
{
    nex[0] = N;
    int p = 0;
    while (p < N && B[p] == B[p + 1]) p ++;
    nex[1] = p;
    int k = 1, L;
    for (int i = 2; i < N; i ++) {
        p = k + nex[k] - 1;
        L = nex[i - k];
        if (i + L <= p) {
            nex[i] = L;
        } else {
            int j = max(p - i + 1, 0);
            while (i + j < N && B[i + j] == B[j]) j ++;
            nex[i] = j;
            k = i;
        }
    }
}

void get_extend()
{
    int p = 0;
    while (p < N && A[p] == B[p]) p ++;
    extend[0] = p;
    int k = 0, L;
    for (int i = 1; i < N; i ++) {
        p = k + extend[k] - 1;
        L = nex[i - k];
        if (i + L <= p) {
            extend[i] = L;
        } else {
            int j = max(p - i + 1, 0);
            while (i + j < N && A[i + j] == B[j]) j ++;
            extend[i] = j;
            k = i;
        }
    }
}

然后讲个例题吧。

比如长沙理工大学2017校赛的H。
题意:大概就是给你一个数,让你从中间拆开成a,b两个数,然后计算a + b后面最多几个0

这题很明显要kmp,但是当场做的时候不知道拓展kmp,然后XJB摸鱼写二分 + kmp,我很傻逼啊。
然后正解是这样的,因为我们肯定要考虑进位嘛,所以要从低位开始考虑,然后先把串反转,然后计算一下补串。然后做一下扩展kmp。同时我们维护两个数组,一个是从i位置开始的连续的0有多少个,还有一个是从i位置开始的连续的9有多少个。
那么我们假设考虑原串的i位置,假设它的extend是j,那么由于不能重叠,所以l = min(i,j),这个时候我们要看两个情况,如果l == i,就是从i位置断开,那么我们要看i + l处有多少个连续的0或者9(不同的情况),然后看前i位是不是全0,如果全0就要看连续0,如果不是那么可以进位,就看连续的9。还有是如果i + l = 串长的时候要看l位置开始的连续的0或者9。

这边可以提交。
https://www.nowcoder.com/acm/contest/1#question

shift-and算法。
感觉这就是个用bitset优化的暴力啊哈???

例题
广西邀请赛17J,16年大连B。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值