hihocoder 1465 循环串匹配问题(后缀自动机)

后缀自动机感觉好万能

tries图和ac自动机能做的,后缀自动机很多也都可以做

这里的循环匹配则是后缀自动机能做的另一个神奇功能

循环匹配意思就是S是abba, T是abb

问‘abb‘, ‘bba‘,‘bab‘在S中出现过多少次。

我们先把T的末尾循环加一遍,变成abbab

然后把问题转换成,求T的每个后缀和S的最长公共子串

如果最长公共子串的长度大于等于T的长度,那么就说明这个后缀匹配成功

做法就是先对S建立一个后缀自动机,然后记录一个状态

(u, l),u表示当前在后缀自动机匹配的位置,l表示最长公共子串的长度

考虑转移的话,就是

如果下一个位置可以匹配,那么u就到相应的位置,l = l+1

答案更新的时候要注意,如果l大于T的长度len,就需要顺着link往前走到第一个能匹配的位置,即第一个maxlen[x] >= len的地方,然后答案加上endpos[x],不然会丢一部分答案。

如果下一个位置不可以匹配,那么u就顺着link边走,走到第一个能匹配的地方,如果找不到,那u就设成起点,l为0

还有一个问题就是串重复的情况,比如说T是aa,那么扩充就会变成aaa,aa和aa重复。

如果串重复的话,那么必定会到同一个状态,所以一个状态标记一下,只更新一遍答案就可以了

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
using namespace std;
int n = 0, len, st;
const int maxL = 1e6 + 100;
int maxlen[2*maxL], minlen[2*maxL], trans[2*maxL][27], slink[2*maxL], lab[2*maxL], son[2*maxL], endpos[2*maxL];
map<int, bool> vis;
int new_state(int _maxlen, int _minlen, int *_trans, int _slink){
    maxlen[n] = _maxlen;
    minlen[n] = _minlen;
    for(int i = 0; i < 26; i++){
        if(_trans == NULL)
            trans[n][i] = -1;
        else
            trans[n][i] = _trans[i];
    }
    slink[n] = _slink;
    return n++;
}

int add_char(char ch, int u){
    int c = ch - ‘a‘;
    int z = new_state(maxlen[u]+1, -1, NULL, -1); lab[z] = 1;
    int v = u;
    while(v != -1 && trans[v][c] == -1){
        trans[v][c] = z;
        v = slink[v];
    }
    if(v == -1){
        minlen[z] = 1;
        slink[z] = 0;
        return z;
    }
    int x = trans[v][c];
    if(maxlen[v] + 1 == maxlen[x]){
        minlen[z] = maxlen[x] + 1;
        slink[z] = x;
        return z;
    }
    int y = new_state(maxlen[v] + 1, -1, trans[x], slink[x]);
    slink[y] = slink[x];
    minlen[x] = maxlen[y] + 1;
    slink[x] = y;
    minlen[z] = maxlen[y] + 1;
    slink[z] = y;
    int w = v;
    while(w != -1 && trans[w][c] == x){
        trans[w][c] = y;
        w = slink[w];
    }
    minlen[y] = maxlen[slink[y]] + 1;
    return z;
}

char str[maxL];
int main()
{
    cin>>str;
    st = new_state(0, 0, NULL, -1);
    int len = strlen(str);
    for(int i = 0; i < len; i++) {
        st = add_char(str[i], st);
    }
    for(int i = 1; i <= n; i++) son[slink[i]]++;
    queue<int> Q;
    for(int i = 1; i <= n; i++) if(son[i] == 0) Q.push(i), endpos[i] = 1;
    while(!Q.empty()){
        int x = Q.front(); Q.pop();
        if(x == 0) continue;
        int y = slink[x];
        son[y]--; endpos[y] += endpos[x];
        if(son[y] == 0){
            if(lab[y]) endpos[y]++;
            Q.push(y);
        }
    }
    int T;
    cin>>T;
    while(T--){
        vis.clear();
        cin>>str;
        int len = strlen(str), ylen = len;
        for(int i = len; i < 2*len-1; i++) str[i] = str[i-len];
        len = 2*len-1;
        int u = 0, l = 0, ans = 0;
        for(int i = 0; i < len; i++){
            int c = str[i] - ‘a‘;
            if(trans[u][c] != -1){
                u = trans[u][c];
                l++;
            } else {
                int y = slink[u];
                while(y != -1){
                    if(trans[y][c] != -1){
                        l = maxlen[y] + 1;
                        u = trans[y][c];
                        break;
                    }
                    u = y;
                    y = slink[u];
                }
                if(y == -1) { u = 0; l = 0; }
            }
            if(l >= ylen){
                int y = slink[u];
                while(maxlen[y] >= ylen) { u = y; y = slink[u]; l = maxlen[u]; }
                if(vis[u]) continue;
                vis[u] = 1;
                ans += endpos[u];
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
时间: 2024-08-28 18:48:15

hihocoder 1465 循环串匹配问题(后缀自动机)的相关文章

hihocoder #1465 : 后缀自动机五&#183;重复旋律8

#1465 : 后缀自动机五·重复旋律8 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”. 小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品.对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多

hihocoder 后缀自动机五&#183;重复旋律8 求循环同构串出现的次数

描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”. 小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品.对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”. 解题方法提示 × 解题方法提示 小Hi:我们已经对后缀自动机比较熟悉了,今天我

字符串匹配:从后缀自动机到KMP

后缀自动机(sam)上的字符串匹配 ==== 我们把相对较短的模式串构造成sam. 对于P="abcabcacab", T[1..i]的后缀,使得它是sam的最长前缀长度: T: b a b c b a b c a b c a a b c a b c a b c a c a b  c 1 1 2 3 1 1 2 3 4 5 6 7 1 2 3 4 5 6 7 5 6 7 8 9 10 4 如果最长前缀长度是|P|,则表示T[1..i]的后缀和P匹配. 内存使用 可能多个trans指针同

hihocoder #1449 : 后缀自动机三&#183;重复旋律6

#1449 : 后缀自动机三·重复旋律6 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数.但是K不是固定的,小Hi想知道对于所有的K的答案. 解题方法提示 × 解题方法提示 小Hi:上次我们已经学习了后缀自动机了,今天我们再来解决一个用到后缀自动机的问题. 小Ho:好!那我们开始吧! 小Hi:现在我们要对K

【hihocoder#1413】Rikka with String 后缀自动机 + 差分

题目链接:http://hihocoder.com/problemset/problem/1413 这个题非常的劲! 首先可以发现,每次只变换一个字符为#,所以每次答案一定会得到相应的包含#的答案,而这个方案是可以直接计算出来的. 假设是$S[i]=$#则会得到$i*(N-i+1)$的子串数. 所以每次的答案可以表示为$sum[root]+i*(N-i+1)-ans[i]$,其中$ans[i]$表示严格经过$i$位置的本质不同的子串,严格的意义即这个本质不同的子串有且仅有一次,且经过$i$: 所

hihoCoder 后缀自动机三&#183;重复旋律6

后缀自动机三·重复旋律6 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数.但是K不是固定的,小Hi想知道对于所有的K的答案. 解题方法提示 输入 共一行,包含一个由小写字母构成的字符串S.字符串长度不超过 1000000. 输出 共Length(S)行,每行一个整数,表示答案. 样例输入 aab 样例输出

hihocoder 1457(后缀自动机+拓扑排序)

题意 给定若干组由数字构成的字符串,求所有不重复子串的和(把他们看成十进制),答案mod(1e9+7) 题解: 类似后缀数组的做法,把字符串之间用':'连接,这里用':'是因为':'的ascii码恰好是9的下一个 然后建立后缀自动机. 之后把其实只要把其中的所有':'边删去,就可以进行转移了 如果x连向了y,边权是c,那么有转移 dp[y] += dp[x]*10 + c*sz[x] 所以只要拓扑排序一下就好 (写这题wa了好几次,主要是在删边建立新图的过程出了问题) #include <ios

hihocoder #1457 : 后缀自动机四&#183;重复旋律7

#1457 : 后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的是小Hi发现了一部名字叫<十进制进行曲大全>的作品集,顾名思义,这部作品集里有许多作品,但是所有的作品有一个共同特征:只用了十个音符,所有的音符都表示成0-9的数字. 现在小Hi想知道这部作品中所有不同的旋律的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0).答案有可能

hihocoder 后缀自动机二&#183;重复旋律5

求不同子串个数 裸的后缀自动机 1 #include<cstring> 2 #include<cmath> 3 #include<iostream> 4 #include<algorithm> 5 #include<cstdio> 6 7 #define ll long long 8 #define N 2000007 9 using namespace std; 10 inline int read() 11 { 12 int x=0,f=1;