Atcoder ARC 077 F - SS KMP 字符串 周期

题意:

定义一个 f(s) 为将字符串s后面加上长度最小的串,使得新串仍时一个形如AA的串。在操作无限次后求 [L,R] 中各个字母出现的次数。保证原串形如AA

数据范围:
  • 2|S|2×105
  • 1lr1018
算法:

问AK爷怎么做,被怒D这不是傻逼题吗。

考虑串是怎么转移的:
  • 设T为字符串S的一个最小border(就是S的一个前缀) 我也不知道这个是不是border
  • 那么转移就非常显然了:
  • SS -> STST -> STSSTS ……
  • 由于是无限次操作,那么只考虑原串的一半也是一样的… 很显然
  • S -> ST -> STS -> STSST……
  • 观察一下发现转移的字符串竟然时“斐波那契”字符串不知道有没有这种字符串,反正长得像就好了
  • 因为斐波那契的第80项左右好像已经大于 1018 所以暴力讨论一下L,R的情况就好了
    题解写成这样估计又要被D
代码:
#include <cstdio>
#include <algorithm>
#include <cstring> 

using namespace std;

const int N = 2e5 + 10;

int n, T, p[N];
char s[N];
long long L, R, num[N][30];

long long fib(long long len, int c) {
    if (len <= n) return num[len][c];
    if (len <= n * 2) return num[n][c] + num[len - n][c];
    long long f1 = num[n][c], f2 = num[n][c] + num[T][c], l1 = n, l2 = n + T;
    while (len > l1 + l2) {
        long long t = f2;
        f2 += f1, f1 = t;
        t = l2, l2 += l1, l1 = t;
    }
    return f2 + fib(len - l2, c);
}

long long calc(long long len, int c) {
    if (len <= n) return num[len][c];
    if (n % T == 0) {
        long long re = 1ll * (len - n) / T * num[T][c] + num[n][c];
        len -= n, len %= T;
        return re + num[len][c];
    }
    else return fib(len, c);
}

int main() {
    scanf("%s%lld%lld", s + 1, &L, &R);
    n = strlen(s + 1) / 2;
    p[1] = 0;
    for (int j = 0, i = 2; i <= n; i ++) {
        for (; j && s[j + 1] != s[i];) j = p[j];
        if (s[j + 1] == s[i]) j ++;
        p[i] = j;
    }
    for (int i = 1; i <= n; i ++) {
        for (int j = 0; j < 26; j ++) num[i][j] = num[i - 1][j];
        num[i][s[i] - 'a'] ++;
    }
    T = n - p[n];
    for (int i = 0; i < 26; i ++) printf("%lld%c", calc(R, i) - calc(L - 1, i), i == 25 ? 10 : 32);
    return 0;
}
    原文作者:KMP算法
    原文地址: https://blog.csdn.net/Cherries_/article/details/74124641
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞