[字符串与矩阵乘法]
[BZOJ 1009][HNOI 2008]GT考试
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0
KMP+矩阵乘法
构造出转移矩阵,注意边界
如果长度为n矩阵乘n次再乘一个初始矩阵。
最后状态是匹配到各个位置上的方案数累加,注意m以后的值是没有转移的。
#include <bits/stdc++.h> #define maxn 1010 using namespace std; int n, m, md; char str[maxn]; int nxt[maxn]; struct Matrix{ int a[21][21]; void clear(){memset(a, 0, sizeof a);} void set(){ clear(); for(int i = 0; i < m; i ++) a[i][i] = 1; } }mat, ans; Matrix operator * (const Matrix& a, const Matrix& b){ Matrix c; c.clear(); for(int i = 0; i < m; i ++) for(int j = 0; j < m; j ++) for(int k = 0; k < m; k ++) c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % md; return c; } Matrix power(Matrix a, int b){ Matrix ret; ret.set(); while(b > 0){ if(b & 1)ret = ret * a; b >>= 1; a = a * a; }return ret; } int main(){ scanf("%d%d%d", &n, &m, &md); scanf("%s", str+1); nxt[0] = nxt[1] = 0; for(int i = 2; i <= m; i ++){ int j = nxt[i-1]; while(j && str[j+1] != str[i]) j = nxt[j]; nxt[i] = str[j+1] == str[i] ? j+1 : 0; } for(int i = 0; i < m; i ++){ for(int j = 0; j < 10; j ++){ int k = i; while(k && (j^48) != str[k+1]) k = nxt[k]; if(str[k+1] == (j^48))mat.a[i][k+1] ++; else mat.a[i][0] ++; } } ans.clear(); ans.a[0][0] = 1; ans = ans * power(mat, n); int ret = 0; for(int i = 0; i < m; i ++) ret = (ret + ans.a[0][i]) % md; printf("%d\n", ret); return 0; }
[BZOJ 1030][JSOI 2007]文本生成器
JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文章—— 也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的L。 ZYX需要指出GW文本生成器 v6生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗?
AC自动机上的DP(貌似和矩阵乘法没什么关系??后面就有啦)
注意状态表示dp[i][j]表示长度为i的字符串匹配到j这个节点的方案数,只有没有标记的后继转移
#include <bits/stdc++.h> #define maxn 5010 using namespace std; typedef long long ll; int n, m; const int md = 10007; ll power_mod(ll a, ll b, ll md){ ll ret = 1; while(b > 0){ if(b & 1) ret = ret * a % md; b >>= 1; a = a * a % md; }return ret; } int t[maxn][26], root = 0, size; void init(){ memset(t, -1, sizeof t); root = size = 0; } bool End[maxn]; char str[maxn]; void Insert(){ int now = root, len = strlen(str+1); for(int i = 1; i <= len; i ++){ int p = str[i] - 'A'; if(t[now][p] == -1) t[now][p] = ++ size; now = t[now][p]; }End[now] = true; } queue<int> Q; int fail[maxn]; void Build_Fail(){ for(int i = 0; i < 26; i ++) if(t[root][i] == -1) t[root][i] = root; else fail[t[root][i]] = root, Q.push(t[root][i]); while(!Q.empty()){ int u = Q.front(); Q.pop(); End[u] |= End[fail[u]]; for(int i = 0; i < 26; i ++){ if(t[u][i] == -1) t[u][i] = t[fail[u]][i]; else fail[t[u][i]] = t[fail[u]][i], Q.push(t[u][i]); } } } int dp[110][maxn], vis[110][maxn]; int DP(int m, int u){ if(m == 0)return 1; if(vis[m][u])return dp[m][u]; vis[m][u] = true; for(int i = 0; i < 26; i ++) if(!End[t[u][i]]) dp[m][u] = (dp[m][u] + DP(m-1, t[u][i])) % md; return dp[m][u]; } int main(){ init(); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++){ scanf("%s", str+1); Insert(); } Build_Fail(); printf("%d\n", (int)((power_mod(26, m, md) - DP(m, root) + md) % md)); return 0; }
[BZOJ 2553]禁忌
一个随机串,如何尽可能多的划分,划分出最多的禁忌串。
考虑一个序列,如何选取线段,使得两两交集为空?练习:3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者
在AC自动机上跑,遇到一个节点贪心。
因为len很大所以考虑矩阵乘法。
新建一个点表示期望。转移的话如果遇到禁忌串转移回根
#include <bits/stdc++.h> #define maxn 100 using namespace std; int n, len, a, size; struct Matrix{ long double a[maxn][maxn]; void clear(){memset(a, 0, sizeof a);} void set(){clear();for(int i = 0; i <= size; i ++)a[i][i] = 1;} }mat; Matrix operator*(const Matrix& a, const Matrix& b){ Matrix c; c.clear(); for(int i = 0; i <= size; i ++) for(int j = 0; j <= size; j ++) for(int k = 0; k <= size; k ++) c.a[i][j] += a.a[i][k] * b.a[k][j]; return c; } Matrix power(Matrix a, int b){ Matrix ret; ret.set(); while(b > 0){ if(b & 1)ret = ret * a; a = a * a; b >>= 1; }return ret; } char str[maxn]; int fail[maxn], t[maxn][26], root; bool End[maxn]; void Insert(){ int now = root, p, len = strlen(str + 1); for(int i = 1; i <= len; i ++){ p = str[i] - 'a'; if(t[now][p] == -1) t[now][p] = ++ size; now = t[now][p]; }End[now] = true; } queue<int> Q; void buildfail(){ for(int i = 0; i < a; i ++){ if(t[root][i] == -1) t[root][i] = root; else Q.push(t[root][i]), fail[t[root][i]] = root; } while(!Q.empty()){ int u = Q.front(); Q.pop(); End[u] |= End[fail[u]]; for(int i = 0; i < a; i ++){ if(t[u][i] == -1)t[u][i] = t[fail[u]][i]; else fail[t[u][i]] = t[fail[u]][i], Q.push(t[u][i]); } } } bool vis[maxn]; void buildmatrix(){ Q.push(root); vis[root] = true; long double tmp = (long double)1 / a; while(!Q.empty()){ int u = Q.front(); Q.pop(); for(int i = 0; i < a; i ++){ int v = t[u][i]; if(End[v]){ mat.a[u][root] += tmp; mat.a[u][size+1] += tmp; } else{ mat.a[u][v] += tmp; if(vis[v])continue; Q.push(v); vis[v] = true; } } } size ++; mat.a[size][size] = 1; } int main(){ memset(t, -1, sizeof t); scanf("%d%d%d", &n, &len, &a); for(int i = 1; i <= n; i ++) scanf("%s", str + 1), Insert(); buildfail(); buildmatrix(); Matrix ans = power(mat, len); printf("%.15lf\n", (double)ans.a[0][size]); return 0; }
[monkey]
也不算矩阵乘法啦,就是个高斯消元。
我们已知f[n]期望为0,因为期望要逆着推,所以最后状态在f[0]中
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #define maxn 1010 using namespace std; char s[maxn]; int str[maxn]; typedef long long ll; ll a[maxn][maxn]; const int md = 1e9 + 7; int n, nxt[maxn]; ll power_mod(ll a, ll b = md - 2){ ll ret = 1; while(b > 0){ if(b & 1)ret = ret * a % md; a = a * a % md; b >>= 1; }return ret; } void Gauss(int n){ for(int i = 0; i <= n; i ++){ for(int j = i; j <= n; j ++){ if(a[j][i]){ for(int k = 0; k <= n + 1; k ++) swap(a[i][k], a[j][k]); ll inv = power_mod(a[i][i]); for(int k = 0; k <= n + 1; k ++) a[i][k] = a[i][k] * inv % md; break; } } if(a[i][i] == 0)continue; for(int j = 0; j <= n; j ++){ if(i == j || a[j][i] == 0)continue; ll nw = a[j][i]; for(int k = i; k <= n + 1; k ++){ a[j][k] -= nw * a[i][k]; a[j][k] %= md; } } } } int main(){ freopen("monkey.in", "r", stdin); freopen("monkey.out", "w", stdout); scanf("%s", s + 1); n = strlen(s + 1); for(int i = 1; i <= n; i ++) str[i] = s[i] - 48; for(int i = 2; i <= n; i ++){ int j = nxt[i - 1]; while(j && str[j + 1] != str[i]) j = nxt[j]; nxt[i] = str[j + 1] == str[i] ? j + 1 : 0; } ll inv2 = power_mod(2); for(int i = 0; i < n; i ++){ a[i][i] = a[i][n + 1] = 1; for(int nw = 0; nw < 2; nw ++){ int j = i; while(j && str[j + 1] != nw)j = nxt[j]; if(str[j + 1] == nw)j ++; a[i][j] += (md - inv2); a[i][j] %= md; } } a[n][n] = 1; Gauss(n); a[0][n + 1] = (a[0][n + 1] + md) % md; printf("%lld\n", a[0][n + 1]); fclose(stdin); fclose(stdout); return 0; }