zoukankan      html  css  js  c++  java
  • [字符串与矩阵乘法]

    [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
    ", 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
    ", (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
    ", (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
    ", a[0][n + 1]);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

      

    给时光以生命,而不是给生命以时光。
  • 相关阅读:
    window对象的方法
    JS注册事件
    JS总结
    JS 扩展方法prototype
    Codeforces 460D Little Victor and Set(看题解)
    Codeforces 891C Envy
    Codeforces 251C Number Transformation
    Codeforces 490F Treeland Tour 树形dp
    Codeforces 605C Freelancer's Dreams 凸包 (看题解)
    几何模板
  • 原文地址:https://www.cnblogs.com/Candyouth/p/5370068.html
Copyright © 2011-2022 走看看