题目
串S仅由KEY三种字符组成(|S|<=30),
最多进行K(0<=K<=1e9)次相邻字符交换,
问能形成的本质不同的串的方案数是多少
思路来源
官方题解
题解
dp[i][e][y][k]表示前i个字母有e个E、y个Y最少交换k次的字符串的方案数
此时也就固定下来有i-e-y个K了
转移枚举当前字符填什么
当前字符填什么,由于知道了历史每个字符都用了几个,
在原串中肯定是优先把靠前的字符换到前面来,
所以,当前字符用什么,在原串里的位置一定唯一,交换次数也能唯一确定
比如,之前已经用过e个E,y个Y,i-e-y个K,
假设当前第i个字符填E,一定用的是串中第e+1个E,
且需要在交换的时候绕过第e+1个E前面的其他字符,
也就是说,如果第e+1个E前面,Y的字符超过y个,那么剩下的Y都需要被绕过,K同理
由于逆序对(可以等价看成012三种数字)的数量,
所以当可交换次数超过n*(n-1)/2时,就等价于可重集全排列的方案数,
注意极限情况分子是30的阶乘,也就是1e32会爆ll,需要开__int128
可交换次数不超过n^2时,dp状态是O(n^5)的,转移枚举三种字符,每种算增量代价是O(n)的,
整体复杂度O(n^6),但由于有一些非法状态,并且会除掉一些系数,
所以暴力枚举也是可以通过的
代码
#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=32,K=N*N;
char s[N];
int n,k,swp;
__int128 fac[N];
ll dp[N][N][N][K];
//dp[i][e][y][l]表示前i个字母有e个E,y个Y(i-e-y个K)最少交换l次的字符串的方案数
map<char,int>cnt;
void add(ll &x,ll y){
x+=y;
}
int main(){
scanf("%s%d",s+1,&swp);
n=strlen(s+1);
fac[0]=1;
rep(i,1,n){
fac[i]=1ll*fac[i-1]*i;
cnt[s[i]]++;
}
if(swp>=n*(n-1)){
printf("%lld\n",fac[n]/fac[cnt['K']]/fac[cnt['E']]/fac[cnt['Y']]);
return 0;
}
dp[0][0][0][0]=1;
rep(i,0,n-1){
int ue=min(cnt['E'],i);
rep(e,0,ue){
int uy=min(cnt['Y'],i-e);
rep(y,0,uy){
int k=i-e-y;
rep(l,0,swp){
if(!dp[i][e][y][l])continue;
for(auto x:{'K','E','Y'}){
map<char,int>mp;
mp['K']=k;mp['E']=e;mp['Y']=y;
int cnt=0;
rep(j,1,n){
if(mp[s[j]]){//被换到前面已经用过的字符
mp[s[j]]--;
}
else{
if(s[j]==x)break;//想要的字符
cnt++;//在当前想要的字符前,换途中必经的字符
}
}
if(l+cnt<=swp){
if(x=='K')add(dp[i+1][e][y][l+cnt],dp[i][e][y][l]);
if(x=='E')add(dp[i+1][e+1][y][l+cnt],dp[i][e][y][l]);
if(x=='Y')add(dp[i+1][e][y+1][l+cnt],dp[i][e][y][l]);
}
}
}
}
}
}
ll ans=0;
rep(i,0,swp){
ans+=dp[n][cnt['E']][cnt['Y']][i];
}
printf("%lld\n",ans);
return 0;
}