算法分析 | 分支限界法 | 字符串移動匹配問題

網上遇到一個很有意思的題目

一.問題描述

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);;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章