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