\[
\texttt{Description}
\]

  • 编程找出符合下列条件的字符串:①字符串中仅包含 0 和 1 两个字符;②字符串的长度为 n ;③字符串中不包含连续重复三次的子串。

\[
\texttt{Solution}
\]

  • 考虑到,将一个串延长至 \(3\) 倍,就成了连续重复三次的串。

  • 于是我们枚举长度在 \(\frac{n}{3}\) 以内的所有 \(0/1\) 串,将其延长至 \(3\) 倍,插入 \(AC\) 自动机中,显然这样插入,所有串的总长是可以承受的,在每个串的结尾,我们给他打上 \(danger\) 标记,也就是说我们不能匹配到包含 \(danger\) 标记的任意串。

  • 考虑 \(dp\)
  • \(f(i,j)\) 表示:考虑到 \(0/1\) 串的前 \(i\) 位,在 \(AC\) 自动机上的节点为 \(j\) 时的字符串数量。
  • 采用刷表法的方式进行 \(dp\) ,考虑当前的节点 \(j\) ,若 \(j\)\(danger\) 标记,则不能计算 \(j\) 的贡献,否则不难得到如下转移:

\[
f(i+1,Next[j][0]) +=f(i,j)
\]

\[
f(i+1,Next[j][1])+=f(i,j)
\]

  • 最后答案即为 \(\sum\limits_{danger(i)=\text{false}}f(n,i)\)

\[
\texttt{Code}
\]

#include <bits/stdc++.h>
const long maxn = 40;
const long times = 3;
const long maxNode = 400000;

long n;

long head, last;
long next[maxNode][2], danger[maxNode], pre[maxNode], self[maxNode], fa[maxNode];

long list[maxNode + 1];

long f[2][maxNode + 1], ans;

void in() {
    scanf("%ld", &n);
}

void prepare() {
    long i, j, k, l, whe, now, s, t;
    for (i = 1; i <= maxNode; i++) next[i][0] = next[i][1] = danger[i] = 0;
    last = 0;
    head = 0;
    fa[0] = 0;
    for (i = 1; i <= n / 3; i++) {
        for (j = 0; j < (1 << i); j++) {
            whe = head;
            for (k = 1; k <= times; k++) {
                for (l = 1; l <= i; l++) {
                    now = (j >> (l - 1)) & 1;
                    if (!next[whe][now]) {
                        self[next[whe][now] = ++last] = now;
                        fa[last] = whe;
                    }
                    whe = next[whe][now];
                }
            }
            danger[whe] = 1;
        }
    }
    s = 0;
    t = 1;
    list[1] = head;
    while (s < t) {
        whe = list[++s];
        for (i = 0; i <= 1; i++)
            if (next[whe][i])
                list[++t] = next[whe][i];
        if (fa[whe]) {
            pre[whe] = next[pre[fa[whe]]][self[whe]];
        } else
            pre[whe] = head;
    }
    for (i = 1; i <= t; i++) {
        whe = list[i];
        for (j = 0; j <= 1; j++)
            if (!next[whe][j])
                next[whe][j] = next[pre[whe]][j];
        danger[whe] |= danger[pre[whe]];
    }
}

void dp() {
    long i, j, k, now, pre;
    for (i = 0; i <= last; i++) f[0][i] = 0;
    f[0][0] = 1;
    now = 0;
    pre = 1;
    for (i = 0; i < n; i++) {
        now = 1 - now;
        pre = 1 - pre;
        for (j = 0; j <= last; j++) f[now][j] = 0;
        for (j = 0; j <= last; j++) {
            if (!danger[j]) {
                f[now][next[j][0]] += f[pre][j];
                f[now][next[j][1]] += f[pre][j];
            }
        }
    }
    ans = 0;
    for (i = 0; i <= last; i++) {
        if (!danger[i]) {
            ans += f[now][i];
        }
    }
}

void work() {
    prepare();
    dp();
}

void out() {
    std::cout << ans << std::endl;
}

int main() {
    in();
    work();
    out();
    return 0;
}

\[
\texttt{Thanks} \ \texttt{for} \ \texttt{watching}
\]

版权声明:本文为cjtcalc原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/cjtcalc/p/12320467.html