codeforces 156C Cipher 组合数学 字符串dp

链接:http://codeforces.com/problemset/problem/156/C
题意:
给定一个字符串,有两种操作。1)选一个位置Sp, 将Sp的字母往后一位(例如a->b),Sp+1的字母往前一位。2)选一个位置Sp, 将Sp的字母往前一位(例如a->b),Sp+1的字母往后一位。(z没有后一位,a没有前一位)
定义两个字符串一致意味着由一个字符串进行n次这种操作后可以变到另一个字符串(n>=0)
要求除了这个给定的字符串之外有多少与其一致的字符串,将数量取模 (109+7)
思路:
感觉这种题真的没怎么见到过。。第一次做还真没有想出思路。。。要好好反思下为什么。。
对于每一个操作都可以看到,对前一个位置所做的操作,与对后一个所做的操作是相反的。再进一步,对于每一个字符都有一个对应的ascii码,而每个操作就是对前一个字符的ascii码 +1 或者 -1 ,并对后一个字符进行相反的操作。那么应该要想到对于每一个字符串来说,与之一致的字符串就意味着他们每一位的ascii码加起来的总和是固定的。
那么就可以进行dp的状态转移了,每个字符串都可以由比其少一位的字符串对应的ascii码值转移过来。
定义 dp[i][j] 为长度为i的ascii码的和为j 的字符串的个数。并且将a的值设为起始值 为 0
状态转移方程: dp[i][j+k]+=dp[i1][j] (0 <= k < 26)
注意还要先动态打一个表出来,不然会超时哦。一个表的计算量必定小于100*100*26*26,直接秒出。
反思:
这道题的思路不算难,为什么自己没有想到?首先,觉得自己对于问题的抽象能力还是太弱,没有看到问题的本质,就拿这题的操作来说,做的时候一味的关注了具体的操作,内在的联系没有想到,对于计算机里这个操作意味着什么,没有思考到。没能将看似无关的东西联系起来。具体这题来说就是感觉对于每一个字符没有转换成数值来看待,过于关注字符本身。其次,对于dp的感觉确实是有点弱,还是要加强。。
以后做题思考的时候应该多思考事物内在的联系,不要总是关注表面的东西,当陷入困境时,要努力跳出来,看看之间的联系或者转换成更加抽象的东西。抽象的来看问题这也是coder的必备技能之一吧。果然还是太弱了。。要加油啊~

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define M 2709
#define mod 1000000007
int ans;
int dp[M][M];
char s[M];
void slove()
{
    dp[0][0] = 1;
    for(int i = 1;i <= 100;i++)
    {
        for(int j = 0;j <= (i-1)*26;j++) 遍历i-1位的所有可能和
        {
            for(int k = 0;k < 26;k++)//在当前位加a~z
            dp[i][j+k] = (dp[i][j+k]+dp[i-1][j])%mod;
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    slove();
    while(t--)
    {
        scanf("%s",s);
        int n = strlen(s);
        int sum = 0;
        for(int i = 0;i < n;i++)
        {
            sum += s[i]-'a';
        }
        printf("%d\n",dp[n][sum]-1);
    }
    return 0;
}

你可能感兴趣的:(codeforces 156C Cipher 组合数学 字符串dp)