序列反转 [百度]【BFS】

题目描述:

给出一个只包含大写英文字母的 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 2N13,1Q100000
输出描述:
对于每个字符串在一行中输出一个整数代表最少的反转次数
示例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
#include
#include
#include
#include
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;
}

你可能感兴趣的:(算法题解,百度,动态规划,算法)