一些字符串(水)题

晚上不想写代码,就水一发博客吧qwq。

P2444 [POI2000]病毒

链接
无限长的字符串就是能在\(ACAM\)上无限跑下去而不会撞到已有的字符串,所以建出\(ACAM\)判环即可。

P3763 [TJOI2017]DNA

链接
建出\(SAM\)之后,由于可以有错误匹配的机会,所以我们可以考虑\(dp\)。令\(dp[i][j]\)表示匹配到\(i\)这个节点时错误了\(j\)次匹配是否可行,当然,写个记忆化搜索就好了。

P3966 [TJOI2013]单词

链接
\(ACAM\)板题……不过我还是一眼看成\(SAM\)……
把所有字符串拼在一起,两两之间新增一个其它字符,然后把这个大字符串插到\(SAM\)里,最后把每个串往上跑就好了。由于每个串都存在,所以连\(parent\)树都不用跳,好写的一批。

P4051 [JSOI2007]字符加密

链接
\(SA\)定义
不过我很好奇,\(SA\)\(09\)年的论文,\(07\)年他们是怎么做的?

P3649 [APIO2014]回文串

链接
回文树板题,树形\(dp\)出每个点(串)的\(size\)然后和\(len\)乘一下就好了,和\(SAM\)板子几乎是一样的。

P4094 [HEOI2016/TJOI2016]字符串

链接
这个问题转化还是比较妙的
首先可以明确,\([a,b]\)中所有子串有意义的其实只有以\(b\)为右端点的那些后缀,所以一种暴力的做法就是枚举左端点再和\([c,d]\)\(lcp\)。对此,我们可以二分答案,显然如果\(maxlcp=x\)的话,任意小于\(x\)\(lcp\)都是可以取到的(一起删去末尾不就好了)。
接下来,我们就把原问题转化成了一个判定性问题:判断\([a,b-mid+1]\)为左端点,b为右端点的子串与\([c,d]\)的子串是否有\(lcp\)大于等于\(mid\)的。这样的话,我们可以在\(SA\)中找到与\([c,d]\)\(lcp\)大于等于\(mid\)的所有串(暂且叫做\(sa_{l…r}\)),那么接下来的问题就是询问\(i∈[a,b-mid+1]\)\(rnk_i∈[l,r]\)\(i\)是否存在。把\(i\)看做下标,\(rnk_i\)看做权值,这就是主席树板子了。

P5108 仰望半月的夜空

链接
考虑贪心,对于长度为\(i\)的子串,我们必然要在\(SA\)上找到第一个长度大于等于\(i\)的子串,这个串的前\(i\)位必然是长度为\(i\)的子串中字典序最小的。在这个基础上,由于位置也要满足最小,那么就是与上述的这个串的\(lcp\)大于等于\(i\)\(sa\)最小的。我们枚举\(i\),由于\(i\)单调递增,那么上述那个串的位置也必然是单调不降的,复杂度就有保证了。

P4036 [JSOI2008]火星人

链接
带插入修改的\(lcp\)……考虑\(lcp\)除了可以\(SA\),也可以用二分+哈希。对此,我们建一颗\(Splay\)出来,每个点维护子树内的\(Hash\)值,对于询问\(lcp\),二分之后在\(Splay\)对应区间判断\(Hash\)是否相等就好了。

P3193 [HNOI2008]GT考试

链接
\(dp[i][j]\)表示当前匹配到大串第\(i\)位、小串第\(j\)位的方案数,转移就是\(dp[i][j] -> dp[i + 1][nxt[j][c]]\)\(c\)是枚举的字符,\(nxt\)表示小串第\(j\)位向\(c\)这个字符走一步会到什么位置,这个可以\(KMP\)预处理。由于状态相同,直接上矩乘就好了。

P4052 [JSOI2007]文本生成器

链接
把上一个题搞到了\(AC\)自动机上,然后不用矩乘了,经过补集转化之后就和上个题就一模一样了。

P3311 [SDOI2014]数数

链接
和上一题的区别是多了个不超过\(N\)的限制。设\(f[i][j]\)表示大串走到第\(i\)位,在\(AC\)自动机上的第\(j\)位且前\(i\)位和\(N\)相同(以下称为卡上界)的方案,\(g[i][j]\)表示不卡上界的方案,那么就有:
\(f[i][j]->f[i+1][ch[j][N_{i+1}]]\)
\(f[i][j]->g[i+1][ch[j][c]]\)
\(g[i][j]->g[i+1][ch[j][c]]\)
\(c\)表示小于\(N_{i+1}\)的数字。然后要注意一下前导零的问题,我一开始就拿着零往\(ACAM\)上跑,结果死活调不出来……
这份代码饱含人类智慧……网上各种代码都又长又难懂……

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>

const int maxn = 1507;
const int mod = 1e9 + 7;

using namespace std;

int n, m;
char s[maxn], t[maxn];
int ch[maxn][10];
int fa[maxn];
int ed[maxn];
int cnt;
int f[maxn][maxn];
int g[maxn][maxn];

inline void insert(char *s)
{
    int x = 0;
    for (int i = 0; s[i]; i++) {
        int c = s[i] - '0';
        if (!ch[x][c]) ch[x][c] = ++cnt;
        x = ch[x][c];
    }
    ed[x] = 1;
}

inline void build()
{
    queue <int> q;
    for (int i = 0; i <= 9; i++) if (ch[0][i]) q.push(ch[0][i]);
    while (!q.empty()) {
        int x = q.front();
        ed[x] |= ed[fa[x]];
        q.pop();
        for (int i = 0; i <= 9; i++)
            if (ch[x][i]) fa[ch[x][i]] = ch[fa[x]][i], q.push(ch[x][i]);
            else ch[x][i] = ch[fa[x]][i];
    }
}

int main(void)
{
    scanf("%s", t + 1);
    int n = strlen(t + 1), m, ans = 0;
    cin >> m;
    while (m--) {
        scanf("%s", s);
        insert(s);
    }
    build();
    int w = 0;
    for (int i = 1; i <= n; i++) { w = ch[w][t[i] - '0']; if(ed[w]) break; f[i][w] = 1; }
    for (int i = 0; i < n; i++)
        for (int j = 0; j <= cnt; j++)
            for (int k = 0; k <= 9; k++) {
                if (ed[ch[j][k]]) continue;
                if (!j && k && (i || k < t[i + 1] - '0')) ++g[i + 1][ch[j][k]] %= mod; //解决前导零的问题
                if (k < t[i + 1] - '0') (g[i + 1][ch[j][k]] += f[i][j]) %= mod;
                (g[i + 1][ch[j][k]] += g[i][j]) %= mod;
            }
    for (int i = 0; i <= cnt; i++) ans = (ans + g[n][i]) % mod;
    cout << (ans + f[n][w]) % mod << endl;
    
    return 0;
}

P4555 [国家集训队]最长双回文串

链接
回文树板题,但我还是选择了更好写的\(Manacher\),不过我还是有点疑惑为什么我最初的思路会错,而且错的很少,得了93分。后来照着题解改了两句话就过了,还是懵逼啊……
不管做没做过优秀的拆分都能看出,我们只要求以每个点为左端点和右端点的最长的回文串,这个可以在跑\(Manacher\)的时候求出来。具体实现还是看其他人的题解吧,毕竟我自己都不知道自己为什么会错……

P2414 [NOI2011]阿狸的打字机

链接
总算把这大坑填了……这题在任务计划里呆了长达\(9\)个月之久……
不过的确是道好题。首先按照题意模拟,小写字母就往下建节点,\(P\)就打\(ed\)标记,\(B\)就往回走,得到一个\(trie\)之后把它建成\(ACAM\)
接下来,对于一次询问,暴力做的话是拿\(y\)串往\(ACAM\)上跑,然后把经过的每一个点的\(fail\)指针都跳一遍,统计有多少个点通过跳\(fail\)经过了\(x\)串的节点。这个问题可以转化成:询问匹配\(y\)串经过的点有多少个在\(fail\)树上\(x\)的子树内。同样的套路,把\(y\)串经过的点看作下标,其在\(fail\)树上的\(dfs\)序看作权值,直接树上主席树即可。真不知道你们是怎么想到离线树状数组的

P1659 [国家集训队]拉拉队排练

链接
注意\(k\)要开\(long~long\)以及只统计奇数回文串。

未完待续……

转载于:https://www.cnblogs.com/star-city/p/10497883.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值