算法分析 | 分支限界法 | 字符串移动匹配问题

网上遇到一个很有意思的题目

一.问题描述

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次)

 

②分析法

修改后字符串
0123

修改后字符串   第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);;
}

 

posted @ 2020-04-03 12:35  心碎人俱乐部  阅读(16)  评论(0)    收藏  举报