字符串练习

来源:互联网 发布:网络校时软件 编辑:程序博客网 时间:2024/06/06 05:18

快要区域赛了,恶补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。

原创粉丝点击