此题刚看到便想会不会有什么规律,找到相邻间的数据间的联系。首先想用什么来表示最基本的状态,我们看题目要求输入的数据是一个字符串,一个字符串我们可以提取出什么数据呢,有字符串的长度和字符串里包含什么字符,字符串的长度可以放入基本状态中,字符串中包含的字符当然不可以完全放入基本状态中,我们要把哪个字符保留下来呢?根据题目的要求如果是尾字母变化太快不适合,首字母比较适合。基本状态包含两个数据我们用一个二维数组来表示。
dp[i][j]表示以第 i 个字母开头,长度为 j 的字符串的个数。此时就看看dp[i][j]和相邻的数据间有什么关系。我们考虑一个具体的例子。 字符串 WZ代表的数值等于以x开头的长度为2的 字符串的个数 加上以 x 开头的长度为1的个数 ,从而得到 dp[i][j] += dp[i+1][j]; dp[i][j] += dp[i+1][j-1]
, 最后计算时要减去相应长度的所有字符串的个数,然后加上给出数据相应字符串的个数,代码注释里有解释。
#include <stdio.h>
#include <string.h>
#define MAX_char 27
#define MAX_length 20
__int64 dp[MAX_char][MAX_length], ans, sum[MAX_length];
char str[MAX_length];
int main()
{
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
for(int i = 1; i < 27; i++) dp[i][1] = 1;
for(int j = 2; j <= 10; j++)
{
for(int i = 27 - j; i >= 1; i--)
{
dp[i][j] += dp[i + 1][j];
dp[i][j] += dp[i + 1][j - 1];
}
}
for(int i = 1; i <= 10; i++)
{
for(int j = 1; j <= 26; j++)
{
sum[i] += dp[j][i];
}
}
scanf("%s", str);
int len = strlen(str);
for(int i = 1; i <= len; i++)
{
ans += sum[i];
}
ans -= sum[len];
int j = 1;
for(int i = len; i >= 1; i--) // 例如 bf 要加上 以a为开头长度为2的字符串的个数和
{ //分别以 c e为开头长度为1的字符串的个数
for(; j < (str[len - i] - 'a') + 1; j++)
{
ans += dp[j][i];
}
j++;
}
for(int i = 0; i < len - 1; i++)
{
if(str[i] >= str[i + 1]) ans = -1;
}
printf("%I64d\n", ans + 1);
return 0;
}
本文介绍了一种使用动态规划解决特定字符串计数问题的方法。通过定义dp[i][j]为以第i个字母开头、长度为j的字符串数量,进而推导出状态转移方程。最终实现了一个C++程序,能够计算给定字符串对应的有效子串数量。
242

被折叠的 条评论
为什么被折叠?



