【经典DP】Good String (不同好串的个数)

题目

描述

We consider a string to be a good string if and only if the string contains no palindromic substrings of length greater than 2
Now we want to know how many different good strings are there for all strings of exactly n length and character set size m (not all characters in the character set are used).

一个好串的定义是当且仅当它不包含长度超过2的回文子串,给定整数n,m,n 表示字符串的长度,m 表示字符集合的大小,找出长度为n的不同的好串的个数

输入

Only one line contains two integer n, m(1 < n < 106, 1 < m ≤109).

输出

Output a integer is number of different good strings,the answer is modulo 1000000007.

Sample Input 1

3 3

Sample Output 1

18

代码长度限制 16KB 
时间限制 1000ms 
内存限制 256 MB

分析

好串不能有如下子结构:

  • xyx:回文串长度为 3
  • xyyx:回文串长度为 4

好串最后三位可以有的子结构:

  • 结构0:xyz:x、y、z 互不相同,dp[i][0]
  • 结构1:xxy:x、y 不同,dp[i][1]
  • 结构2:xyy:x、y 不同,dp[i][2]

特殊情况

if (n == 1) {
	return m;
}
// 其实可以归于 n == 2 return m+m*(m-1)
if (n == 2 && m == 1) {
	return 1;
}
if (n == 2 && m >= 2) {
	return m+m*(m-1);
}

// 其实可以直接使用转移方程了
if (m == 1 && n >= 3) {
	return 0;
}
if (m == 2 && n == 3) {
	return 4;
}

转移方程

// dp[i][0] 表示长度为 i 且最后三个字母是 xyz 的好串个数
// dp[i][1] 表示长度为 i 且最后三个字母是 xyy 的好串个数
// dp[i][2] 表示长度为 i 且最后三个字母是 xxy 的好串个数

// dp[i-1][0] xyz --> yz[k] k除了x、y有 m-2 种选法
// dp[i-1][1] xxy --> xy[k] k除了x、y有 m-2 种选法
// dp[i-1][2] xyy --> yy[k] 无法得到结构0
dp[i][0] = dp[i-1][0]*(m-2) + dp[i-1][1]*(m-2);

// dp[i-1][0] xyz --> yz[k] 无法得到结构1
// dp[i-1][1] xxy --> xy[k] 无法得到结构1
// dp[i-1][2] xyy --> yy[k] k除了y有 m-1 种选法
dp[i][1] = dp[i-1][2]*(m-1);

// dp[i-1][0] xyz --> xy[k] k只能选择y
// dp[i-1][1] xxy --> xy[k] k只能选择y
// dp[i-1][2] xyy --> yy[k] 无法得到结构2
dp[i][2] = dp[i-1][0] + dp[i-1][1];

代码

#include <iostream>
using namespace std;

int main()
{
    int mod = 1e9+7;
    int n, m;
    cin >> n >> m;
    if (n == 1) {
        cout << m;
        return 0;
    }
    if (n == 2) {
        cout << m + m*(m-1);
        return 0;
    }

    // dp[i][0] 表示长度为 i 且最后三个字母是 xyz 的好串个数
    // dp[i][1] 表示长度为 i 且最后三个字母是 xyy 的好串个数
    // dp[i][2] 表示长度为 i 且最后三个字母是 xxy 的好串个数
    int dp[n+1][3];
    dp[3][0] = m*(m-1)*(m-2);
    dp[3][1] = m*(m-1);
    dp[3][2] = m*(m-1);

    for (int i = 4; i <= n; ++ i) {
        dp[i][0] = (dp[i-1][0]*(m-2) + dp[i-1][1]*(m-2)) % mod;
        dp[i][1] = dp[i-1][2]*(m-1) % mod;
        dp[i][2] = (dp[i-1][0] + dp[i-1][1]) % mod;
    }
    cout << (dp[n][0] + dp[n][1] + dp[n][2]) % mod;
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值