網上遇到一個很有意思的題目
一.問題描述
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);;
}