题目描述
此时相望不相闻,愿逐月华流照君。
一纸情书,到底蕴含了多少倍的爱情呢?
I love you, not only for what you are, but for what I am when I am with you.
输入描述:
共一行:一封若干个字符的情书(大小写不敏感)。
情书不会超过684594个字符(大写、小写字母)。
输出描述:
共一行:包含一个整数,即iloveyou在情书中作为子序列出现的次数。
由于答案可能很大,请输出对20010905取模后的值。
示例1
输入
IloveyouNotonlyforwhatyouareButforwhatIamWhenIamwithyouIloveyouNotonlyforwhatYouhavemadeofyourselfButforwhatYouaremakingofme
输出
2864
思路:竟然没看出来这是个动态规划QwQ。
我们定义dp[i][j]是从字符串前i个字符串中匹配,匹配到iloveyou中第j位出现的次数,这里为了方便
表示下标是从1开始的,其实从0从1开始都一样,为了方便理解就从1开始了。
那么显然:
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 1 ] + ( s [ i ] = = i ) dp[i][1] = dp[i - 1][1] + (s[i] == i) dp[i][1]=dp[i−1][1]+(s[i]==i)在c/c++中在一个式子中判等会变成0或1
第i位如果和第一个字符i相等的话,那么dp[i][1] 就是从前i-1个字符中匹配第一个字符i出现的次数加
上这次的。
那么剩下的就是:
d
p
[
i
]
[
2
]
=
d
p
[
i
−
1
]
[
2
]
+
(
s
[
i
]
=
=
l
)
∗
d
p
[
i
−
1
]
[
1
]
dp[i][2] = dp[i - 1][2] + (s[i] == l) * dp[i-1][1]
dp[i][2]=dp[i−1][2]+(s[i]==l)∗dp[i−1][1]
d
p
[
i
]
[
3
]
=
d
p
[
i
−
1
]
[
3
]
+
(
s
[
i
]
=
=
o
)
∗
d
p
[
i
−
1
]
[
2
]
dp[i][3] = dp[i - 1][3] + (s[i] == o) * dp[i-1][2]
dp[i][3]=dp[i−1][3]+(s[i]==o)∗dp[i−1][2]
d
p
[
i
]
[
4
]
=
d
p
[
i
−
1
]
[
4
]
+
(
s
[
i
]
=
=
v
)
∗
d
p
[
i
−
1
]
[
3
]
dp[i][4] = dp[i - 1][4] + (s[i] == v) * dp[i-1][3]
dp[i][4]=dp[i−1][4]+(s[i]==v)∗dp[i−1][3]
d
p
[
i
]
[
5
]
=
d
p
[
i
−
1
]
[
5
]
+
(
s
[
i
]
=
=
e
)
∗
d
p
[
i
−
1
]
[
4
]
dp[i][5] = dp[i - 1][5] + (s[i] == e) * dp[i-1][4]
dp[i][5]=dp[i−1][5]+(s[i]==e)∗dp[i−1][4]
d
p
[
i
]
[
6
]
=
d
p
[
i
−
1
]
[
6
]
+
(
s
[
i
]
=
=
y
)
∗
d
p
[
i
−
1
]
[
5
]
dp[i][6] = dp[i - 1][6] + (s[i] == y) * dp[i-1][5]
dp[i][6]=dp[i−1][6]+(s[i]==y)∗dp[i−1][5]
d
p
[
i
]
[
7
]
=
d
p
[
i
−
1
]
[
7
]
+
(
s
[
i
]
=
=
o
)
∗
d
p
[
i
−
1
]
[
6
]
dp[i][7] = dp[i - 1][7] + (s[i] == o) * dp[i-1][6]
dp[i][7]=dp[i−1][7]+(s[i]==o)∗dp[i−1][6]
d
p
[
i
]
[
8
]
=
d
p
[
i
−
1
]
[
8
]
+
(
s
[
i
]
=
=
u
)
∗
d
p
[
i
−
1
]
[
7
]
dp[i][8] = dp[i - 1][8] + (s[i] == u) * dp[i-1][7]
dp[i][8]=dp[i−1][8]+(s[i]==u)∗dp[i−1][7]
这里我们只挑 d p [ i ] [ 2 ] = d p [ i − 1 ] [ 2 ] + ( s [ i ] = = l ) ∗ d p [ i − 1 ] [ 1 ] dp[i][2] = dp[i - 1][2] + (s[i] == l) * dp[i-1][1] dp[i][2]=dp[i−1][2]+(s[i]==l)∗dp[i−1][1](也就是第一个)来说就行了,剩下的以此类推。
对于之前dp[i][1]的时候,我们能够知道大部分,但是为什么我们这里要乘以一个dp[i-1][1]呢?
我们知道如果第i位与第二个字符l相同的话,那么在1到i-1中所有能跟第1个字符i相同的位置上,都能和当前第i个字符l组成一个子序列(题目中说了是子序列,不是子串),所以要乘以一个dp[i-1][1]
之后的几项类推即可
这里用了滚动数组的方式实现,遍历顺序就不讲了(这个应该是背包的基础吧)
奥对了,还有一点,就是初始状态dp[0][0]我们要初始化成什么
根据定义可知,从前0个字符串中去跟第0个字符(实际不存在)匹配的方案数,那么这种状态是有一个
情况的,所以dp[0][0]要初始化成1
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 684594 + 10;
const int MOD = 20010905;
const char str[] = {"ailoveyou"} ;
int dp[10];
char s[N];
int main(void)
{
scanf("%s",s + 1);
for(int i = 1 ; s[i] ; ++ i)
{
if(s[i] >= 'A' && s[i] <= 'Z')
s[i] = 'a' + s[i] - 'A';
}
dp[0] = 1;
for(int i = 1 ; s[i] ; ++ i)
{
for(int j = 8 ; j ; -- j)
{
dp[j] = (dp[j] + (s[i] == str[j]) * dp[j - 1]) % MOD;
}
}
cout << dp[8] % MOD << endl;
return 0;
}