CodeChef:Hasan and boring classes(组合数学 & 字符串)

Hasan is a computer science student, he likes his study field, especially programming and algorithms, but he doesn’t like attending classes at all since they are very boring, especially that the quality of education in his college is extremely low, but sometimes he is forced to attend in order to maintain his attendance rate otherwise he will be prevented from doing final exams!

Currently Hasan is attending `communication skills` class and he is now feeling very bored especially that he thinks such course should not belong to a computer science program, luckily he remembered a problem that one of his friends has given him so he decided to spend the time thinking about it, the problem states:

Given a string S of length N of lower English letters, find how many strings T exists of length N of lower English letters such that T is palindrome and hamming distance between T and S is at most K. since the answer is large find the remainder modulo 109 + 7

Since you also don’t like `communication skills` classes you decided to solve this problem too.

 

Input

The first line of the input contains an integer T denoting the number of test cases.

each test-case is described with two lines, the first line of each test-case contains two integers N and K.

The second line of each test-case will contains the string S of length N.

 

Output

For each test case, output a single line containing the answer to the test-case.

 

Constraints

  • 1 ≤ T ≤ 10,000
  • 1 ≤ N ≤ 300,000
  • 1 ≤ sum of N in all test-cases ≤ 1,000,000
  • 0 ≤ K ≤ N
  • String S will consist of English lower letters.

 

Example

Input:
2
3 2
abc
3 1
abc

Output:
76
2

 

Explanation

in second test-case the only possible strings are "aba" and "cbc"

题意:给一个字符串,要求修改至多K个字符使它变成回文串,问方案数。

思路:对称的字符数有Fix个,不对称的有UnFix个。显然Fix那些一修改就要修改两个,这就可以先预处理Fix那些方案数,Fixed[i]表示至多修改i个Fix字符令其是对称的方案数。UnFix里面,每对字符要么修改其中一个,要么两个都修改,那么我们枚举只修改一个的,那么其余的两个都要修改的数量立刻可以求出来,具体见代码。 这种题一般都要固定某些枚举值,找到这些可以枚举的变量就行了。

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 3e5+3;
const LL mod = 1e9+7;
char s[maxn];
LL inv[maxn+3]={1,1}, fac[maxn+3]={1,1}, fi[maxn+3]={1,1};
LL f25[maxn]={1,25}, f24[maxn]={1,24}, f2[maxn]={1,2};
LL Fixed[maxn];
void init()
{
    for(int i=2; i<=maxn-3; ++i)
    {
        f25[i] = f25[i-1]*25%mod;
        f24[i] = f24[i-1]*24%mod;
        f2[i] = f2[i-1]*2%mod;
        fac[i] = fac[i-1]*i%mod;
        inv[i] = (mod-mod/i)*inv[mod%i]%mod;
        fi[i] = fi[i-1]*inv[i]%mod;
    }
}
LL C(LL n, LL m)//求组合数
{
    if(n<m) return 0;
    return fac[n]*fi[m]%mod*fi[n-m]%mod;
}
LL solve(int k, LL fix, LL unfix)
{
    LL ans = 0;
    Fixed[0] = 1LL;
    for(int i=1; i<=k; ++i)//预处理fix那些
    {
        Fixed[i] = Fixed[i-1];
        if(i%2==0) Fixed[i] = (Fixed[i] + C(fix, (LL)i/2)*f25[i/2]%mod)%mod;
    }
    for(int i=0; i<=k && i<=unfix; ++i)//unfix那些
    {
        int r = i+(unfix-i)*2;
        if(r <= k)
        {
            LL tmp = C(unfix, (LL)i)*f2[i]%mod;//2^i的意思是这i对字符,每对各有两种修改方案。
            tmp = tmp*f24[unfix-i]%mod;//24^(unfix-i)的意思是其余的unfix对字符,各有24种修改方案。
            ans = (ans + tmp*Fixed[k-r]%mod)%mod;//剩余的修改名额分配给fix那些。
        }
    }
    return ans;
}
int main()
{
    init();
    int T, n, k;
    LL fix, unfix;
    scanf("%d",&T);
    while(T--)
    {
        fix = unfix = 0;
        scanf("%d%d",&n,&k);
        scanf("%s",s);
        for(int i=0; i<n/2; ++i)
        {
            if(s[i] != s[n-i-1]) ++unfix;
            else ++fix;
        }
        if(n&1)//奇数长度时中间那个字符另外讨论。
        {
            LL ans = solve(k, fix, unfix);
            ans = (ans + solve(k-1, fix, unfix)*25%mod)%mod;
            printf("%lld\n",ans);
        }
        else
            printf("%lld\n",solve(k, fix, unfix));
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值