问题描述
小U非常喜欢字符'R',并且定义字符串的权值为字符'R'的出现次数。现在她手上有一个长度为n的字符串s,由字符'R'和'B'组成。她想知道,在所有长度为n、仅由字符'R'和'B'组成的字符串中,字典序不小于字符串s的字符串的权值之和是多少。由于结果可能很大,答案需要对取模。
例如,字符串"RRBRB"的权值为 3,因为其中包含 3 个字符'R'。
测试样例
样例1:
输入:
n = 3 ,s = "RBR"
输出:7
样例2:
输入:
n = 4 ,s = "RRBB"
输出:12
样例3:
输入:
n = 2 ,s = "BB"
输出:4
题目分析
我们需要计算所有长度为 n 且字典序不小于给定字符串 s 的字符串中,字符 'R' 的出现次数之和。由于结果可能很大,答案需要对 10^9+7 取模。
关键算法
快速幂: 由于结果需要对 取模,我们可以使用快速幂来加速计算。快速幂(Fast Power)是一种算法,用于快速计算底数的n次幂。其时间复杂度为O(log2N),与朴素的O(N)相比,效率有了极大的提高。快速幂算法的基本思想是通过将指数分解为二进制形式,从而减少乘法的次数。
递推法
-
动态规划:
- 我们可以使用动态规划来解决这个问题。定义
dp[i][j]表示前i个字符中,字符'R'出现j次的字符串的权值之和。 - 初始状态:
dp[0][0] = 1,表示空字符串的权值为 1。 - 状态转移:对于每个位置
i,我们可以选择放'R'或'B',并更新dp数组。
- 我们可以使用动态规划来解决这个问题。定义
-
计算权值之和:
- 对于每个位置
i,如果当前字符是'B',则我们需要计算所有可能的字符串的权值之和。 - 如果当前字符是
'R',则直接累加权值
- 对于每个位置
组合数
如果要求取所有字典序大于等于原串的字符串,我们可以从头开始枚举每一个字符,这时会分为两种情况:
s[i]为'R':说明没得改,如果这个字符改为'B',则不能满足题设条件。s[i]为'B':这时我们可以尝试将这个字符改为'R',这样我们会发现,修改该字符后,后方所有字符无论怎样修改,该字符串的字典序都是大于原串的,我们直接使用组合数求出后面所有字符的(情况总数) * (期望的R字符数量)
很显然,修改情况数即为 ,而期望则为i前面所有的R字符数量+后面所有字符数量/2。
这里的除以2可以使用乘法逆元,但由于前方的情况总数是2的次幂,这里的除以2可以直接被约分。
PS:乘法逆元:在 的情况下(p为大于2的质数),可以被转化为,其具有相应的数学特性。
参考代码
const int mod = 1e9+7;
ll qmi(ll a, ll b) {
if(b < 0) return 0;
ll res = 1;
while(b) {
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int solution(int n, string s) {
ll cnt = 0, ans = 0;
s = ' ' + s;
for(int i=1;i<=n;i++) {
if(s[i] == 'B') {
ans = (ans + (cnt + 1) * qmi(2,n-i) % mod + qmi(2,n-i-1) * (n - i) % mod) % mod;
} else cnt ++;
}
ans = (ans + cnt) % mod;
return ans;
}
总结
最后,让我们用MarsCode AI为本题进行一次总结,看看它有何高见。
关键点
- 快速幂:通过快速幂函数
qmi,可以高效地计算幂次,避免溢出。- 动态更新权值:在遍历字符串时,根据当前字符是
'R'还是'B',动态更新权值之和。- 取模操作:在每次更新权值时,都对
10^9+7取模,确保结果不会溢出。总结
当前代码通过遍历字符串
s,并根据当前字符是'R'还是'B',动态更新权值之和。使用快速幂函数qmi来高效计算幂次,并在每次更新权值时对10^9+7取模,确保结果的正确性。希望这个总结能帮助你更好地理解当前代码的实现思路。如果有任何疑问或需要进一步的解释,请随时告诉我。