题目描述:
给出一个只包含大写英文字母的 A,B,C 的字符串,每次可以选择该字符串的一个前缀,然后将其反转,请问至少反转几次,才能使字符串中较小的字母都排在较大的字母之前,即字母 A 都在字母 B,C 之前,字母 B 都在字母 C 之前。
输入描述:
第一行给出两个整数
N
,
Q
,
N,Q,
N,Q,代表字符串的长度和字符串个数
接下来
Q
Q
Q 行每行给出一个长度为
N
N
N 的字符串
2
≤
N
≤
13
,
1
≤
Q
≤
100000
2\leq N\leq13,1\leq Q\leq100000
2≤N≤13,1≤Q≤100000
输出描述:
对于每个字符串在一行中输出一个整数代表最少的反转次数
示例1
输入
5 1
ABCBA
输出
3
说明
ABCBA -> BCBAA -> CBBAA -> AABBC
示例2
输入
5 1
CCBAB
输出
2
说明
CCBAB -> BABCC -> ABBCC
思路:
可能的字符串最多是 3 13 = 1594323 3^{13}=1594323 313=1594323,所以可以把所有的可能性都存下来。因为Q个字符串长度相同,所以考虑:从所有有序的状态向外bfs,得多所有状态的最少反转次数。之后的Q次询问直接查找即可。
// 本题为考试多行输入输出规范示例,无需提交,不计分。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N = 1594323+10;
int n,Q;
int dp[N];
string s="";
queue<string> q;
// 将每个状态映射为数字,也就是转化为3进制
int get_num(string s) {
int res = 0;
for (int i = 0; i < n; i++)
res = res * 3 + s[i] - 'A';
return res;
}
void build(string s, int cur, bool st)
{
if (cur == n)
{
if (st)
{
dp[get_num(s)] = 0;
q.push(s);
}
return ;
}
for (int i = 0; i < 3; i++)
{
if(s.size()<cur+1) s += char('A' + i);
else s[cur]=char('A'+i);
build(s, cur+1, st && (cur == 0 || s[cur-1]<=s[cur]));
}
}
void bfs()
{
while (!q.empty())
{
string tmp = q.front(); q.pop();
int cur = get_num(tmp);
for (int i = 1; i <= n; i++)
{
s=tmp;
reverse(s.begin(), s.begin()+i);
int nxt = get_num(s);
if (dp[nxt] > dp[cur] + 1)
{
dp[nxt] = dp[cur] + 1;
q.push(s);
}
}
}
}
int main(){
cin>>n>>Q;
memset(dp,0x3f,sizeof dp);
build(s, 0, true); //得到所有有序状态
bfs();
string str;
while (Q--)
{
cin>>str;
cout<<dp[get_num(str)]<<endl;
}
return 0;
}