hdu 4055 Number String
Number String
http://acm.hdu.edu.cn/showproblem.php?pid=4055
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
The signature of a permutation is a string that is
computed as follows: for each pair of consecutive elements of the permutation,
write down the letter 'I' (increasing) if the second element is greater than the
first one, otherwise write down the letter 'D' (decreasing). For example, the
signature of the permutation {3,1,2,7,4,6,5} is "DIIDID".
Your task is as follows: You are given a string describing the signature of many possible permutations, find out how many permutations satisfy this signature.
Note: For any positive integer n, a permutation of n elements is a sequence of length n that contains each of the integers 1 through n exactly once.
Your task is as follows: You are given a string describing the signature of many possible permutations, find out how many permutations satisfy this signature.
Note: For any positive integer n, a permutation of n elements is a sequence of length n that contains each of the integers 1 through n exactly once.
Input
Each test case consists of a string of 1 to 1000
characters long, containing only the letters 'I', 'D' or '?', representing a
permutation signature.
Each test case occupies exactly one single line, without leading or trailing spaces.
Proceed to the end of file. The '?' in these strings can be either 'I' or 'D'.
Each test case occupies exactly one single line, without leading or trailing spaces.
Proceed to the end of file. The '?' in these strings can be either 'I' or 'D'.
Output
For each test case, print the number of permutations
satisfying the signature on a single line. In case the result is too large,
print the remainder modulo 1000000007.
Sample Input
II
ID
DI
DD
?D
??
Sample Output
1
2
2
1
3
6
Hint
Permutation {1,2,3} has signature "II".
Permutations {1,3,2} and {2,3,1} have signature "ID".
Permutations {3,1,2} and {2,1,3} have signature "DI".
Permutation {3,2,1} has signature "DD".
"?D" can be either "ID" or "DD".
"??" gives all possible permutations of length 3.
题意:
给出长为n的一个字符串,根据这个字符串构造长为n+1的数字串
字符为‘I’,下一个数字要比这一个数字大
字符为‘D’ ,下一个数字要比这一个数字小
字符为‘?’,下一个数字没有限制
问数字串有多少种方案
一般套路:
f[i][j]表示长为i的串中,最后一个数字为j的方案数
若字符为‘I’,f[i][j]= Σ f[i-1][x] 1 <= x < j
若字符为‘D’,f[i][j]= Σ f[i-1][x] j <= x <= i-1
若字符为‘?’,f[i][j]=Σ f[i-1][x] 1 <= x <= i-1
我们虽然根据 字符保证了相邻两个数字的大小关系,但并没有保证数字串里前i-1个数字没有数字j
这就有了后效性。怎么办?
给状态增加含义:必须选前i-1个数字
我们将过程想象为一个一个填数字的过程
那么由f[i-1][]向f[i][]的转移,就是在末尾(i位置)填上数字i
(因为状态的定义是必须选前i-1个数字)
那么岂不是只能填数字i?字符为‘D’时不就错了吗?第二维不就没有用吗?
我们考虑第二维j(要填的最后一个数字),
我们是否可以将填数字i转换到填数字j,
那么状态转移的时候,就要想如何填j使相邻数字的大小关系不变
将前i-1个数字>=j的都+1,这样就拿出了j,放在最后面
所以,上方状态转移成立
时间复杂度?O(n³) TLE
前缀和优化,时间复杂度O(n²)
其实很简单,照着原方程x的取值范围写就行
1 <= x < j 前缀和就是sum[i-1][j-1]
j <= x <= i-1 前缀和就是 sum[i-1][i-1]-sum[i-1][j-1]
1 <= x <= i-1 前缀和就是sum[i-1][i-1]
前缀和的更新: sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
前缀和优化代码
#include<cstdio> #include<cstring> #define mod 1000000007 using namespace std; int len,f[1002][1002],sum[1002][1002]; char s[1001]; int main() { while(scanf("%s",s+1)!=EOF) { memset(f,0,sizeof(f)); len=strlen(s+1); f[1][1]=1; sum[1][1]=1; for(int i=2;i<=len+1;i++) for(int j=1;j<=i;j++) { if(s[i-1]=='I') f[i][j]=sum[i-1][j-1]; else if(s[i-1]=='D') f[i][j]=((sum[i-1][i-1]-sum[i-1][j-1])%mod+mod)%mod; else f[i][j]=sum[i-1][i-1]; sum[i][j]=(sum[i][j-1]+f[i][j])%mod; } printf("%d\n",sum[len+1][len+1]); } }
未优化代码
#include<cstdio> #include<cstring> #define mod 1000000007 using namespace std; int len,f[1002][1002]; char s[1001]; int main() { while(scanf("%s",s+1)!=EOF) { memset(f,0,sizeof(f)); len=strlen(s+1); f[1][1]=1; for(int i=2;i<=len+1;i++) for(int j=1;j<=i;j++) { if(s[i-1]=='I') for(int k=1;k<j;k++) f[i][j]+=f[i-1][k]; else if(s[i-1]=='D') for(int k=j;k<i;k++) f[i][j]+=f[i-1][k]; else for(int k=1;k<i;k++) f[i][j]+=f[i-1][k]; } int ans=0; for(int i=1;i<=len+1;i++) ans+=f[len+1][i]; printf("%d\n",ans); } }