解题 [HNOI2008] GT考试

题目:[HNOI2008] GT考试

阿申准备报名参加 GT 考试,准考证号为 N 位数X1,X2Xn (0Xi9),他不希望准考证号上出现不吉利的数字。
他的不吉利数字A1,A2,,Am (0Ai9)M 位,不出现是指 X1,X2Xn 中没有恰好一段等于 A1,A2,,AmA1X1 可以为 0
阿申想知道不出现不吉利数字的号码有多少种,输出模 K 取余的结果。

对于全部数据,N109M20K1000


这个题的做法是这样的:

这个题涉及到字符串匹配,题目中的“不出现”,也就代表着不吉利数字无法匹配到 m

1、最暴力的想法是将准考证号枚举出来,然后在进行字符串匹配,这样的复杂度是 O(10N×N) 的。

2、当使用 KMP 做字符串匹配的时候,我们可以考虑这样一个过程:

  • X 匹配到第 i 位,A 匹配到第 k 位,在下一个位置发生了失配。
  • 然后跳 border 到下一个状态,X 匹配到第 i+1 位,A 匹配到第 j 位。

也就是由 qi(k)+cqi+1(j) 的转移。考虑到只需要求方案数,我们设 fi(j) 为当 X 匹配到第 i 位,A 匹配到第 j 位的合法的号码的方案数(当然 j 永远不会等于 m)。

但是由 qi(k)+cqi+1(j) 的转移,会存在 0 或多种。那么考虑枚举字符 c,来得到“由位置 k 的下一个位置失配到位置 j 的方案数”,记为 g(k,j)

for (int k = 0, j; k < m; k ++ )
	for (char c = '0'; c <= '9'; c ++ ) {
		for (j = nxt[k]; j && a[j + 1] != c; j = nxt[j]);
		g[k][j + (a[j + 1] == c)] ++;
	}

这样的话,就可以得到状态 f 的转移 fi(j)×g(j,k)fi+1(k)

那么根据 f 的定义来看,就可以达到最终答案 i=0m1fn(i),总时间复杂度为 O(nm2)

参考代码(40 pts):

#include <bits/stdc++.h>

using namespace std;

int n, m, K, ans, nxt[21], g[21][21], dp[10010][21];
char a[21];

int main() {
	cin >> n >> m >> K >> (a + 1);
	
	for (int i = 2, j = 0; i <= m; i ++ ) {
		for (j = nxt[i - 1];j && a[j + 1] != a[i]; j = nxt[j]);
		nxt[i] = a[j + 1] == a[i] ? j + 1 : 0;
	}
	
	for (int i = 0, j; i < m; i ++ ) {
		for (char c = '0'; c <= '9'; c ++ ) {
			for (j = i; j && a[j + 1] != c; j = nxt[j]);
			g[i][j + (a[j + 1] == c)] ++;
		}
	}
	
	dp[0][0] = 1;
	for (int i = 1; i <= n; i ++ ) {
		for (int j = 0; j < m; j ++ ) {
			for (int k = 0; k < m; k ++ ) {
				dp[i][j] = (dp[i - 1][k] * g[k][j] + dp[i][j]) %K;
			}
		}
	}
	
	for (int i = 0; i < m; i ++ ) ans = (ans + dp[n][i]) %K;
	cout << ans << '\n';
	return 0;
}

3、优化转移

分析状态 f 的转移 i=0m1fi(j)×g(j,k)fi+1(k),从中可以得到 fi(1,j)×g(j,k)fi+1(1,k),即一个矩乘的式子 fi×g=fi+1

然后对转移进行矩阵加速幂,时间复杂度为 O(m3log2n) 的。

参考代码(100 pts):

#include <bits/stdc++.h>
using namespace std;


const int M = 31;

int n, m, K, res, nxt[M];

char s[M];

struct matrix {
    int a[M][M], n, m;
    matrix() {
        n = m = 0;
        memset(a, 0, sizeof(a));
    }
} F, G;

matrix operator* (const matrix &a, const matrix &b) {
    matrix tmp;
    tmp.n = a.n, tmp.m = b.m;
    for (int i = 0; i < tmp.n; i ++ ) {
        for (int j = 0; j < tmp.m; j ++ ) {
            for(int k = 0; k < a.m; k ++ ) {
                tmp.a[i][j] = (tmp.a[i][j] + a.a[i][k] * b.a[k][j] %K) %K;
            }
        }
    }
    return tmp;
}

matrix Power(matrix a, int k) {
    matrix ans = a; 
    for (k --; k; a = a * a, k >>= 1)
        if (k & 1) ans = ans * a;
    return ans;
}

int main()
{
    cin >> n >> m >> K >> (a + 1);
    
    F.n = 1, F.m = G.m = G.n = m;

    for (int i = 2, j = 0; i <= m; i ++ ) {
		for (j = nxt[i - 1];j && a[j + 1] != a[i]; j = nxt[j]);
		nxt[i] = a[j + 1] == a[i] ? j + 1 : 0;
	}
	
	for (int i = 0, j; i < m; i ++ ) {
		for (char c = '0'; c <= '9'; c ++ ) {
			for (j = i; j && a[j + 1] != c; j = nxt[j]);
			g[i][j + (a[j + 1] == c)] ++;
		}
	}

    F.a[0][0] = 1;

    F = F * Power(G, n);

    for (int i = 0; i < m; i ++ ) res = (res + F.a[0][i]) %mod; 

    cout << res << '\n';
    return 0;
}
posted @   Ciaxin  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示