题目大意:有两个长度为n的字符串a,b,每次操作可以选出字母表中的一个字母,并使a中所有该字母分别变成任意的字母,最多操作k次,问最多有多少个数对l,r使a[l],a[r]之间的满足a=b的子串数量最多
1<=n<=1e5;0<=k<=10,a中最多有10个不同的字母
思路:因为操作的次数越多,答案的数量肯定不会变小,所以从a中选出的不同字母的数量k应为min(k,m(a中不同字母数)),然后我们发现从最多10个字母中选k个字母最多只有C(5,10)=252种可能,那么我们对每一种选择出来的字母,遍历字符串,替换相应字符并统计即可。
对于从m个字母中选择k个字母,可以用递归的方法,对于m个字母中的第i个,先考虑不选这个字母,之后回溯时考虑选这个字母,再回溯时,将其置回不选择的状态即可。如果当前已经遍历到第m个字母,且选择的字母数等于k,就统计答案,否则直接回溯。
对于确定选取的字母后遍历a,我们只要找到a=b的位置就计数,直到不满足a=b,就加上计数*(计数+1)/2,每次统计答案后取最大值即可
//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans = 0;
string s1, s2;
string app;
int n, k;
bool vis[30];
int len;
void count(int itap, int cnt)
{//遍历到了app字符串的第itap个字母,当前已选择cnt个字母
if (cnt > k)
return;//选择的超过k个,不合法
if (itap == len)
{//所有字母已经遍历结束
if (cnt == k)
{//如果选择的字母数正好等于k,统计答案
ll temp = 0;
ll now = 0;//符合条件的区间长度
for (int i = 0; i < n; i++)
{
if (s1[i] == s2[i] || vis[s1[i] - 'a'])
{//本来就等于,或者可以修改
now++;
}
else
{
temp += now * (now + 1) / 2;//区间结束,统计答案
now = 0;
}
}
temp += now * (now + 1) / 2;//数组结束也要统计
ans = max(ans, temp);
}
return;
}
count(itap + 1, cnt);//不选当前字母
vis[app[itap] - 'a'] = 1;//选当前字母
count(itap + 1, cnt + 1);
vis[app[itap] - 'a'] = 0;//回溯后重置
}
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
{
cin >> n >> k;
cin >> s1 >> s2;
unordered_set<char>se;//记录都有哪些不同字母
for (int i = 0; i < n; i++)
{
se.insert(s1[i]);
}
app.clear();
for (unordered_set<char>::iterator it=se.begin(); it != se.end(); it++)
{
app.push_back(*it);//用字符串记录不同字母,便于遍历
}
len = app.length();
ans = 0;
k = min(k, len);
memset(vis, 0, sizeof vis);
count(0, 0);
cout << ans << endl;
}
return 0;
}