其他分享
首页 > 其他分享> > Topcoder 10748 - StringDecryption(dp)

Topcoder 10748 - StringDecryption(dp)

作者:互联网

题面传送门

神仙题。

首先这个两次加密略微有点棘手,咱们不妨先从一次加密的情况入手考虑这个问题。显然,一次加密等价于将加密过的序列划分成若干段,每一段都是 \(xd\) 的形式表示这一段中有 \(x\) 个字符 \(d\)。那么我们就可以设 \(dp_{i}\) 表示原字符串长度为 \(i\) 的前缀可以由多少个字符串经过一次加密得到,转移就枚举上一段结尾 \(j(j\le i-2)\) 然后转移即可,只不过 \(j\) 可以转移到 \(i\) 需要满足两个条件:一是上一段的结尾与这一段的结尾不同,即 \(s_i\ne s_j\),二是这一段不能出现前导零,即 \(s_{j+1}\ne 0\)。

接下来考虑两次加密的情况,我们还是按照一次加密的情况枚举上一段的结尾 \(j\),这样第一次解密出来就是 \(x\) 个字符 \(d\),其中 \(x\) 是 \(s[j+1...i-1]\) 连接而成的 \(i-j-1\) 位数,\(d\) 是 \(s_j\) 表示的数。以 \(x=6,d=5\) 为例,第二次解密共有以下划分方法:

受到这个思想的启发,我们可以设 \(dp_{i,d,k}\) 表示当前解密了原字符串的前 \(i\) 位,在第一次解密出来的字符串中进行划分,划分出来最后一段的最后一位为 \(d\),当前第一次解密出来的字符串中是否有字符还没有划分为完整的一段的情况为 \(k\) 的方案数。转移还是枚举原字符串中上一段的右端点位置为 \(j\),上一段最后一个字符 \(p\),我们假设 \(s[j+1...i-1]\) 组成的数为 \(x\),\(s_i=d\),那么可以分以下情况:

最终答案即为 \(dp_{n,s_n,0}\)。

时间复杂度 \(10n^2\)

const int MAXN=500;
const int MOD=1e9+9;
int n,dp[MAXN+5][11][2],pw10[MAXN+5];
struct StringDecryption{
	int decrypt(vector<string> code){
		string s;
		for(int i=0;i<code.size();i++) s=s+code[i];
		n=s.size();s=" "+s;dp[0][10][0]=pw10[0]=1;
		for(int i=1;i<=n;i++) pw10[i]=10ll*pw10[i-1]%MOD;
		for(int i=1;i<=n;i++){
			int sum=0,dig=s[i]-'0';
			for(int j=i-2;~j;j--){
				sum=(sum+1ll*pw10[i-2-j]*(s[j+1]-'0'))%MOD;
				if(s[j+1]=='0'||s[j]==s[i]) continue;
//				printf("%d %d %d\n",i,j,sum);
				for(int k=0;k<=10;k++){
					if(dig!=0) dp[i][k][1]=(dp[i][k][1]+dp[j][k][0])%MOD;
					dp[i][k][1]=(dp[i][k][1]+dp[j][k][1])%MOD;
					if(dig!=k){
						if(dig!=0){
							if(!(j==i-2&&sum==1)) dp[i][dig][1]=(dp[i][dig][1]+1ll*(sum-2+MOD)*dp[j][k][0])%MOD;
							dp[i][dig][1]=(dp[i][dig][1]+1ll*(sum-1+MOD)*dp[j][k][1])%MOD;
						}
						dp[i][dig][0]=(dp[i][dig][0]+dp[j][k][1])%MOD;
						if(dig!=0&&!(j==i-2&&sum==1)) dp[i][dig][0]=(dp[i][dig][0]+dp[j][k][0])%MOD;
					}
				}
			}
//			printf("%d %d\n",dp[i][dig][0],dp[i][dig][1]);
		} return dp[n][s[n]-'0'][0];
	}
};

标签:字符,33,解密,StringDecryption,划分,一段,Topcoder,dp
来源: https://www.cnblogs.com/ET2006/p/topcoder-10748.html