Number String
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2495 Accepted Submission(s): 1224
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.
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’.
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.
题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4055
一开始考虑在前i个排好的情况下,在数列任意位置插入第i+1个。但是如果在排好的数列中插入会改变前面的递增递减关系,不好用dp进行解题。
那么我们设dp[i][j]为数列第i个数字为j。
如果此位和前一位的关系为I,即前一位小于此位,那么前一位可能是任意小于j的数字。所以dp[i][j]=dp[i-1][j-1]+dp[i-1][j-2]+…+dp[i-1][1]。
如果此位和前一位的关系为D,即前一位大于此位,那么前一位可能是任意大于等于j的数字。所以dp[i][j]=dp[i-1][i-1]+dp[i-1][i-2]+…+dp[i-1][j]。
那么这样做,每次j最大只循环到i的话,大于i小于n的数是如何能排到前面去的?而且是怎么做到整个数列没有重复元素的?
就比如下一位是D,我们前面排好了2 1 3,现在所求为dp[4][2],我们的方法是把数列里面大于等于2的全部加1,小于2的不变,再把2排到最后,这样并不会改变前面排好序列的大小关系。那么我们得到的序列就是3 1 4 2。
这样既不会出现相同元素,也可以解释为什么这么递推可以把大于当前循环最大值i的数排在前面。
当然如果直接进行循环是个n三次方的复杂度。所以我们需要优化前缀和降低复杂度为n方。
以下AC代码。
#include
#include
#include
using namespace std;
const long long mod=1000000007;
char str[1005];
long long dp[1005][1005],sum[1005][1005];
int main(){
while(scanf("%s",str)!=EOF){
int n;
n=strlen(str);
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
dp[1][1]=1;
sum[1][1]=1;
for(int i=2;i<=n+1;i++){
for(int j=1;j<=i;j++){
if(str[i-2]=='I'||str[i-2]=='?')
dp[i][j]=(dp[i][j]+sum[i-1][j-1])%mod;
if(str[i-2]=='D'||str[i-2]=='?'){
int num;
num=(sum[i-1][i-1]-sum[i-1][j-1]+mod)%mod;
dp[i][j]=(dp[i][j]+num)%mod;
}
sum[i][j]=(dp[i][j]+sum[i][j-1])%mod;
}
}
printf("%lld\n",sum[n+1][n+1]);
}
return 0;
}