题目传送门
题意简述
小R定义了一种特别的字符串,称之为可爱串。当且仅当该字符串包含子序列 "chi",并且不包含子串 "chi" 时,称为可爱串。子序列是指字符串中不连续的一段,而子串则必须是连续的字符。目标是计算长度为 n 的、仅由 'c', 'h', 'i' 三种字符组成的字符串中,有多少是可爱串。结果需要对 10^9+7取模。
解题思路
-
基本概念:
-
子序列 "chi":字符串中存在字符 'c'、'h'、'i',且顺序为 'c' 在 'h' 之前,'h' 在 'i' 之前。
-
不包含子串 "chi":字符串中不能有连续的 "chi"。
-
动态规划:
-
使用动态规划来计算满足条件的字符串数量。
-
定义三个数组 f, g, h:
-
f[i] 表示长度为 i 的字符串中可爱串的数量。
-
g[i] 用于辅助计算,表示长度为 i 的字符串中包含子序列 "ch" 的数量。
-
h[i] 表示长度为 i 的字符串中包含子串 "chi" 的数量。
-
递推关系:
-
g[i] 的计算:通过递推关系,考虑在长度为 i-1 的字符串基础上添加一个字符,形成新的子序列 "ch"。
-
f[i] 的计算:在长度为 i-1 的基础上,考虑添加一个字符形成新的可爱串。
-
h[i] 的计算:通过递推关系,计算包含子串 "chi" 的数量。
4. 结果计算:
- 最终结果为 f[n] - h[n],即长度为 n 的字符串中可爱串的数量减去包含子串 "chi" 的数量。
代码实现
public class Main {
private static final int MOD = 1000000007;
public static long fastexp(long base, long n, long mod) {
long answer = 1;
while (n > 0) {
if (n % 2 == 1) {
answer = (answer * base) % mod;
}
base = (base * base) % mod;
n /= 2;
}
return answer;
}
public static int solution(int n) {
long[] f = new long[n + 1];
long[] g = new long[n + 1];
long[] h = new long[n + 1];
for (long i = 2; i <= n; i++) {
g[(int) i] = (g[(int) i - 1] * 2 + (i - 1) * fastexp(2, i - 2, MOD)) % MOD;
}
for (long i = 3; i <= n; i++) {
f[(int) i] = ((f[(int) i - 1] * 3) % MOD + g[(int) i - 1]) % MOD;
}
for (long i = 3; i <= n; i++) {
h[(int) i] = (fastexp(3, i - 3, MOD) + h[(int) i - 1] * 3 - h[(int) i - 3]) % MOD;
}
return (int) ((f[n] - h[n] + MOD) % MOD);
}
public static void main(String[] args) {
System.out.println(solution(4) == 3);
System.out.println(solution(5) == 24);
System.out.println(solution(6) == 126);
}
}
时间复杂度分析
该算法的时间复杂度为 𝑂(𝑛),因为我们需要遍历长度为 n 的数组来计算 f, g, h 的值。每个数组的计算都涉及到简单的递推关系,因此整体复杂度是线性的。
用到的算法和数据结构
-
动态规划:用于计算满足条件的字符串数量。
-
快速幂:用于计算幂次,帮助在递推关系中处理指数增长。
详细分析
在实现过程中,我们使用动态规划来计算满足条件的字符串数量。通过定义 f, g, h 三个数组,我们能够有效地计算出可爱串的数量。g[i] 和 h[i] 的计算帮助我们在递推过程中考虑子序列和子串的影响。最终结果通过 f[n] - h[n] 得到,确保我们只计算符合条件的可爱串。通过对结果取模,我们能够处理大数问题,确保结果在计算过程中不会溢出。