算法分析 | 分支限界法 | 字符串移动匹配问题
网上遇到一个很有意思的题目
一.问题描述
1.

2.分析解
a.很明显是一颗解空间树.先来分析一下这个问题的结构
比如
待测字符串: s1="1234";
目标字符串: s2="3124";
待测字符串s1长度为4,有4*3*2*1=24种排列组合
先思考最多需要几次修改,能 任一排列-->任一排列
①穷举法:
1次:算上本身只有4个分支
2次:有1+3+9=13个分支
3次:有1+3+9+27=40个分支,>24
最多要3次(size-1次)

②分析法
| 0 | 1 | 2 | 3 |
修改后字符串 第3位 来自于待测字符串 最晚移动 的字符 or 无移动
修改后字符串 第2位 来自于待测字符串 第二晚移动 的字符 or 无移动
修改后字符串 第1位 来自于待测字符串 第三晚移动 的字符 or 无移动
修改后字符串 第0位 来自于待测字符串未移动的字符
也就是说:4字符的串,从任一排列-->任一排列,一定会经过[0,3]次移动
推广:
N个字符的字符串,需要[0,N-1]次移动,可到达全部排列方式
解空间树是N-1叉树,深度为N-1(根节点深度=0)
b.那么应该用深度遍历(回溯法)还是广度遍历(分支限界法)呢?
一开始用的是回溯法,发现不是很合适.实际解是一个最小值.
如果实际解很小.深度遍历却已经达到很深的叶结点.而且需要不断地递归、恢复父结点状态.十分麻烦
应该按照贪心的原则,从浅层-->深层
二.代码分析
1.全局变量
using namespace std;
#include<iostream>
#include<vector>
#include<queue>
#include<numeric>
//定义全局变量
class Str
{
public:
Str() {};
Str(string _s, int _count) :s(_s), count(_count) {};
string s; //需要检测的字符串
int count = 0; //当前状态的修改次数
};
2.部件函数,用来执行" 将选中字符挪到末尾 "操作
//将字符串当前位置字符->End
string Move2End(int index, Str str)
{
char temp = str.s[index];
str.s.erase(str.s.begin() + index);
str.s.push_back(temp);
return str.s;
}
3.分支限界法求解问题,按照要求返回值
//按回溯法寻找最优解,count初值=0,记录操作次数
int MoveStrTrack(string s1, string s2)
{
//判断两个字符串是否相等
if (s1 == s2)
{
return 0;
}
//建立队列
queue<Str>q;
Str livenode,newnode;
newnode = Str(s1, 0); //根节点修改次数=0
q.push(newnode); //先进根节点,开动循环
int sizeOfStr = s1.size();
while (!q.empty())
{
livenode = q.front();
q.pop();
//约束条件
if (livenode.count >= sizeOfStr-1) //若到达叶结点,检测后跳过本次循环
{
if (livenode.s==s2)
{
return livenode.count;
}
continue; //跳过本次循环
}
//终止条件
if ((!q.empty())&&(q.front().count>=sizeOfStr)) //队列不空&&到达叶结点的子节点.此时不存在s1->s2的可能性
{
return -1;
}
//开始搜索子节点
for (int i = 0; i < sizeOfStr - 1; i++) //只遍历第[0,size-2]个字符
{
newnode.s=Move2End(i, livenode);
newnode.count = livenode.count+1; //子节点的修改数比父结点多1
if (newnode.s == s2) //两个串相等
{
return newnode.count;
}
else
{
q.push(newnode);
}
}
}
return 0; //执行到此处,说明队列空了也没有找到匹配,返回0
}
4.调用函数
//调用
void MoveStr() {
string s1;
string s2;
cout << "输入字符串s1: "; cin >> s1; cout << endl;
cout << "输入字符串s2: "; cin >> s2; cout << endl;
//判断长度是否一致
int cmp_length_flag = true;
while (cmp_length_flag)
{
if (s1.size() == s2.size())
{
cmp_length_flag = false; //长度相等终止循环
}
else
{
system("cls");
cout << "两个字符串长度不等,请重新输入/n";
}
}
//调用输出
cout << s1 << endl << s2 << endl;
cout << "s1的最少移动次数: " << MoveStrTrack(s1, s2);;
}

浙公网安备 33010602011771号