算法 字典序问题 动态规划解法(计算机算法与分析 1-2 王晓东)

百度清一色的数学方法, 惟一的动态规划还是有问题的
数学方法无法参悟,于是参考了动态规划方法的建表,自己又写了一遍

首先是建表,表格大小7*27,其中第一行为了方便是闲置的

行代表字符串长度,列代表字符串起始字母;
	
第一行(i=1)第一列(j=0)代表以a开头长度为1的字符串个数,显然是1;
第一行均为长度为1的字符串,个数均为1;
第一行最后一列记录这一行的总数,这里是26;

第二行第一列代表以a开头长度为2的字符串个数,显然是a[1][26]-a[1][0],以b开头长度为2的字符串个数是a[1][26]-a[1][0]-a[1][1],以此类推;
第二行开始需要注意以z开头没有长度超过1的字符串,y开头没有长度超过2的字符串...等等;
同样的,最后一列记录这一行总的个数;

以此类推,直到最后一行,建表完成。

接下来是计算部分,首先定义了一些变量:

ans:编码值-1;
pos:纵向移动,最多移动5次;
siz:字符串长度;
mov:横向移动,最多移动25次;
cnt:记录横向断点;

再来看一下具体如何计算:

首先计算当前行前一行到第一行的最后一列值之和(第一行例外,为0,代码中不体现)
再计算当前行从a列到首字母前一个字母那列之和
到了首字母则向右上方移动一格,计算从那一格到第二个字母前一列的和
到了第二个字母那行再向右上移动...直到到达最后一个字母
为了追求代码简洁因此所有字符串中出现的字符都是不计入的,但是末位要+1,因此最后得到的ans再加一就是正确答案。

下面举个例子:cegik
ans=a[1][26]+a[2][26]++a[3][26]+a[4][26]+a[5][0]+a[5][1]+a[4][3]+a[3][5]+a[2][7]+a[1][9]
编码:ans+1

代码

int main(){
    string s;
    int dp[7][27];
    for(int i=2;i<7;i++)
        for(int j=0;j<27;j++)
            dp[i][j]=0;
    for(int j=0;j<26;j++)
        dp[1][j]=1;
    dp[1][26]=26;
    for(int i=2;i<7;i++)
        for(int j=0;j<=26-i;j++){
            dp[i][j]=dp[i-1][26];
            for(int k=0;k<=j;k++)
                dp[i][j]-=dp[i-1][k];
            dp[i][26]+=dp[i][j];
        }
    while(cin>>s){
        int siz=s.size(),ans=0,cnt=0;
        for(int i=1;i<=siz-1;i++)
            ans+=dp[i][26];
        for(int pos=0;pos<siz;pos++){
            for(int mov=cnt;mov<s[pos]-'a';mov++){
                ans+=dp[siz-pos][mov];
                cnt++;
            }
            cnt++;
        }
        cout << ans+1 << endl;
    }
    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值