zoukankan      html  css  js  c++  java
  • BZOJ 2085 [POI2010] Hamsters

    题面

    Description

    Tz养了一群仓鼠,他们都有英文小写的名字,现在Tz想用一个字母序列来表示他们的名字,只要他们的名字是字母序列中的一个子串就算,出现多次可以重复计算。现在Tz想好了要出现多少个名字,请你求出最短的字母序列的长度是多少。

    Input

    输入:第一行n(1<=n<=200)和m(1<=m<=10的9此方),n表示有多少个仓鼠,m表示Tz希望出现名字的次数,接下来n行,每行都是仓鼠的名字(中间没有空格)。

    Output

    输出:一行,最短的字母序列的长度。

    Sample Input

    4 5
    monika
    tomek
    szymon
    bernard
    Sample Output
    23
    

    Sample Output

    23
    

    题目大意

    略.

    题解

    这一题的题面漏了一个条件: 字符串之间两两不相包含. 由于(n le 200), 不难想到让这些名字两两作一次KMP, 得到一个转移矩阵. 我们考虑如何让(m)个名字在所求的串中出现. 建立一个初始矩阵: 对角线为每个字符串的长度, 其余位置均为(+infty). 对初始矩阵与转移矩阵作(m - 1)次转移(使用倍增的方法, 类似于快速幂, 但不是矩阵乘法, 而是对应的行向量和列向量加起来取(min)值). 得到的矩阵中, (res_{i j})表示开始第一个名字为(i), 最后出现的名字为(j)的需要的最少字符数量.
    (res_{n imes n})中取min即可.
    一些细节需要注意, 已经在代码中标注.

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    const int N = 200, LEN = (int)1e5;
    const long long INF = (long long)1e15;
    
    namespace KMP
    {
    	inline void initialize(char *str, int len, int *pre)
    	{
    		pre[0] = -1;
    		for(int i = 1; i < len; ++ i)
    		{
    			int p = pre[i - 1];
    			for(; ~ p && str[p + 1] ^ str[i]; p = pre[p]);
    			if(str[p + 1] == str[i])
    				++ p;
    			pre[i] = p;
    		}
    	}
    
    	inline int match(char *s, int len, char *t, int *pre)
    	{
    		int p = -1;
    		for(int i = 0; i < len; ++ i)
    		{
    			for(; ~ p && t[p + 1] ^ s[i]; p = pre[p]); // 假如s和t是同一个串的话, 则要特判
    			if(t[p + 1] == s[i] && (s != t || p + 1 < i))
    				++ p;
    		}
    		return p + 1;
    	}
    }
    
    struct matrix
    {
    	long long a[N][N];
    	int n;
    
    	inline matrix(int _n)
    	{
    		n = _n;
    	}
    
    	inline matrix friend operator *(const matrix &A, const matrix &B)
    	{
    		static matrix res(A.n);
    		for(int i = 0; i < A.n; ++ i)
    			for(int j = 0; j < A.n; ++ j)
    			{
    				res.a[i][j] = INF;
    				for(int k = 0; k < A.n; ++ k)
    					res.a[i][j] = std::min(A.a[i][k] + B.a[k][j], res.a[i][j]);
    			}
    		return res;
    	}
    };
    
    inline void power(matrix A, int x, matrix &res)
    {
    	for(; x; x >>= 1, A = A * A)
    		if(x & 1)
    			res = res * A;
    }
    
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("BZOJ2085.in", "r", stdin);
    	// freopen("BZOJ2085.out", "w", stdout);
    	#endif
    	int n, m;
    	scanf("%d%d", &n, &m);
    	static char str[N][LEN];
    	static int pre[N][LEN];
    	for(int i = 0; i < n; ++ i)
    		scanf("%s", str[i]), KMP::initialize(str[i], strlen(str[i]), pre[i]);
    	static matrix A(n);
    	for(int i = 0; i < n; ++ i)
    		for(int j = 0; j < n; ++ j)
    			A.a[i][j] = strlen(str[j]) - KMP::match(str[i], strlen(str[i]), str[j], pre[j]);
    	static matrix res(n);
    	for(int i = 0; i < n; ++ i)
    		for(int j = 0; j < n; ++ j)
    			res.a[i][j] = i ^ j ? INF : strlen(str[i]);
    	power(A, m - 1, res); // m要减1
    	long long ans = INF;
    	for(int i = 0; i < n; ++ i)
    		for(int j = 0; j < n; ++ j)
    			ans = std::min(ans, res.a[i][j]);
    	printf("%lld", ans);
    }
    
    
  • 相关阅读:
    省市县三级联动
    ajax的封装及调用(版本二-面向对象)
    Vue Element-Ui 改变el-Input背景样式
    StringUtils工具类常用方法汇总:判空、转换、移除、替换、反转。
    keep-alive 用法 及activated,deactivated这两个生命周期函数
    深入理解vue中的slot与slot-scope
    Vue2.0 v-for 中 :key 到底有什么用?
    Element-UI中关于table表格的样式操作
    子组件props接受父组件传递的值 能修改吗?
    vue iviem UI grid布局
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7110300.html
Copyright © 2011-2022 走看看