汉诺塔问题
题目
给定一个整数n,代表汉诺塔游戏中从小到大放置的n个圆盘,假设开始时所有的圆盘都放在左边的柱子上,想按照汉诺塔游戏的要求把所有的圆盘都放到右边的柱子上,实现函数打印最优移动轨迹。
进阶题目可参考原书
代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
void func(int n, string from, string mid, string to)
{
if (n == 1)
cout << "move from" + from + " to " + to << endl;
else
{
func(n - 1, from, to, mid);
func(1, from, mid, to);
func(n - 1, mid, from, to);
}
}
void hanoi(int n)
{
if (n > 0)
func(n, "left", "mid", "right");
}
//判断一个数组是否为最优轨迹中的一个状态,若是,判断为第几个状态
int process(vector<int> arr, int i, int from, int mid, int to)
{
if (i == -1)
return 0;
if (arr[i] != from && arr[i] != to)
return -1;
if (arr[i] == from)
return process(arr, i - 1, from, to, mid);
else
{
int rest = process(arr, i - 1, mid, from, to);
//转移的顺序要分清楚,如果错误则最后结果出错
if (rest == -1)
return -1;
return (1 << i) + rest;
}
}
int step1(vector<int> arr)
{
if (arr.size() < 1)
return -1;
return process(arr, arr.size() - 1, 1, 2, 3);
}//时间复杂度可以达到O(N),但递归对栈的使用使空间复杂度达到o(N)
//非递归的方法
int step2(vector<int> arr)
{
if (arr.size() < 1)
return -1;
int from = 1;
int mid = 2;
int to = 3;
int i = arr.size() - 1;
int res = 0;
int tmp = 0;
while (i >= 0)
{
if (arr[i] != from && arr[i] != to)
return -1;
if (arr[i] == to)
{
res += 1 << i;
tmp = from;
from = mid;
}
else
{
tmp = to;
to = mid;
}
mid = tmp;
i--;
}
return res;
}
int main()
{
int n;
cin >> n;
vector<int>in(n, 0);
for (int i = 0; i < n; i++)
cin >> in[i];
hanoi(n);
int res1 = step1(in);
int res2 = step2(in);
cout << res1 << res2 << endl;
getchar();
return 0;
}
最长公共子序列问题
题目
给定两个字符串str1和str2,返回两个字符串的最长公共子序列。
代码
详细分析过程参考原书
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
vector<vector<int>> getdp(string str1, string str2)
{
int row = str1.size();
int col = str2.size();
vector<vector<int>> dp(row, vector<int>(col, 0));
dp[0][0] = str1[0] == str2[0] ? 1 : 0;
for (int i = 1; i < row; i++)
{
dp[i][0] = max(dp[i - 1][0], str1[i] == str2[0] ? 1 : 0);
}
for (int j = 1; j < col; j++)
{
dp[0][j] = max(dp[0][j - 1], str1[0] == str2[j] ? 1 : 0);
}
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
if (str1[i] == str2[j])
dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
return dp;
}
string maxLCS(string str1, string str2)
{
if (str1.size() < 1 || str1.size() < 1 || str1 == "" || str2 == "")
return "";
vector<vector<int>> dp = getdp(str1, str2);
int m = str1.size() - 1;
int n = str2.size() - 1;
int index = dp[m][n];
string res(index, 'a');
index--;
while (index >= 0)
{
if (n > 0 && dp[m][n] == dp[m][n - 1])
n--;
else if (m > 0 && dp[m][n] == dp[m - 1][n])
m--;
else
{
res[index--] = str1[m];
m--;
n--;
}
}
return res;
}
int main()
{
string s1, s2;
getline(cin, s1);
getline(cin, s2);
string res = maxLCS(s1, s2);
cout << res << endl;
getchar();
return 0;
}
/*dp矩阵的获取比较简单,dp[i][j]的取值只有三种,分别为dp[i-1][j],
dp[i][j-1],dp[i-1][j-1]+1;
重建最长公共子序列的过程的关键点是dp[i][j] = dp[i-1][j-1]+1,剩余
的两种情况都可以略过不去考虑*/
最长公共字串问题
题目
给定两个字符串str1和str2,返回两个字符串的最长公共子串。
要求:如果str1的长度为M,str2长度为N,实现时间复杂度为O(M×N),额外空间复杂度为O(1)的方法。
代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
vector<vector<int>> getdp(string s1, string s2)
{
int row = s1.size();
int col = s2.size();
vector<vector<int>> dp(row, vector<int>(col, 0));
for (int i = 0; i < row; i++)
{
if (s1[i] == s2[0])
dp[i][0] = 1;
}
for (int j = 0; j < col; j++)
{
if (s1[0] == s2[j])
dp[0][j] = 1;
}
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
if (s1[i] == s2[j])
dp[i][j] = dp[i - 1][j - 1] + 1;
}
}
return dp;
}
string lcst1(string s1, string s2)
{
if (s1.size() < 1 || s2.size() < 1 || s1 == "" || s2 == "")
return "";
vector<vector<int>> dp = getdp(s1, s2);
int row = s1.size();
int col = s2.size();
int end = 0;
int maxV = 0;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (dp[i][j] > maxV)
{
end = i;
maxV = dp[i][j];
}
}
}
return s1.substr(end - maxV + 1, maxV);
}
string lcst2(string s1, string s2)
{
if (s1.size() < 1 || s2.size() < 1 || s1 == "" || s2 == "")
return "";
int row = s1.size();
int col = s2.size();
int m = 0;
int n = col - 1;
int maxV = 0;
int end = 0;
while (m < row)
{
int i = m;
int j = n;
int len = 0;
while (i < row && j < col)
{
if (s1[i] != s2[j])
len = 0;
else
len++;
if (len > maxV)
{
end = i;
maxV = len;
}
i++;
j++;
}
if (n > 0)
n--;
else
m++;
}
return s1.substr(end - maxV + 1, maxV);
}
int main()
{
string s1, s2;
getline(cin, s1);
getline(cin, s2);
string res1 = lcst1(s1, s2);
string res2 = lcst2(s1, s2);
cout << res1 << " " << res2 << endl;
getchar();
return 0;
}
/*思路与最长公共子序列问题基本上一致,只是在dp矩阵更新是可以将算法
的空间复杂度压缩至O(1),只记录最终位置和最大公共字串的长度即可*/
最小编辑代价
题目
给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,返回将str1编辑成str2的最小代价。
代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
int minCost1(string s1, string s2, int ic, int dc, int rc)
{
if (s1.size() < 1 || s2.size() < 1)
return 0;
int row = s1.size() + 1;
int col = s2.size() + 1;
vector<vector<int>> dp(row, vector<int>(col, 0));
for (int i = 1; i < row; i++)
{
dp[i][0] = dc * i;
}
for (int j = 1; j < col; j++)
dp[0][j] = ic * j;
//找到dp的更新方法
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
if (s1[i - 1] == s2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = dp[i - 1][j - 1] + rc;
dp[i][j] = min(dp[i][j], dp[i][j - 1] + ic);
dp[i][j] = min(dp[i][j], dp[i - 1][j] + dc);
}
}
return dp[row - 1][col - 1];
}
//空间压缩
int minCost2(string s1, string s2, int ic, int dc, int rc)
{
if (s1.size() < 1 || s2.size() < 1)
return 0;
int more = s1.size() > s2.size() ? s1.size() : s2.size();
int less = s1.size() > s2.size() ? s2.size() : s1.size();
string longs = s1.size() > s2.size() ? s1 : s2;
string shorts = s1.size() > s2.size() ? s2 : s1;
if (s1.size() < s2.size())
{
int tmp = ic;
ic = dc;
dc = tmp;
//如果s2较长,那么交换ic和dc的值??
}
vector<int>dp(less + 1, 0);
for (int i = 1; i <= less; i++)
dp[i] = ic * i;
for (int i = 1; i <= more; i++)
{
int pre = dp[0]; //pre表示左上角的值
dp[0] = dc * i;
for (int j = 1; j <= less; j++)
{
int tmp = dp[j]; //dp[j]未更新前先保存下来
if (longs[i - 1] == shorts[j - 1])
dp[j] = pre;
else
dp[j] = pre + rc;
dp[j] = min(dp[j], dp[j - 1] + ic);
dp[j] = min(dp[j], tmp + dc);
pre = tmp;
}
}
return dp[less];
}
int main()
{
string s1, s2;
getline(cin, s1);
getline(cin, s2);
int ic, dc, rc;
cin >> ic >> dc >> rc;
int res1 = minCost1(s1, s2, ic, dc, rc);
int res2 = minCost2(s1, s2, ic, dc, rc);
cout << res1 << " " << res2 << endl;
getchar();
return 0;
}
字符串的交错组成
题目
给定三个字符串str1,str2和aim,如果aim包含且仅包含来自str1和str2的所有字符,而且在aim中属于str1的字符之间保持原来在str1中的顺序,属于str2的字符之间保持原来在str2中的顺序,那么称aim是str1和str2的交错组成。实现一个函数,判断aim是否是str1和str2的交错组成。
代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
bool isCross1(string s1, string s2, string aim)
{
if (s1.size() < 1 || s2.size() < 1 || aim.size() < 1)
return false;
if (s1.size() + s2.size() != aim.size())
return false;
int row = s1.size();
int col = s2.size();
vector<vector<bool>> dp(row + 1, vector<bool>(col + 1, false));
dp[0][0] = true;
for (int i = 1; i <= row; i++)
{
if (s1[i - 1] != aim[i - 1])
break;
dp[i][0] = true;
}
for (int j = 1; j <= col; j++)
{
if (s2[j - 1] != aim[j - 1])
break;
dp[0][j] = true;
}
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if ((s1[i - 1] == aim[i + j - 1] && dp[i - 1][j])
|| (s2[j - 1] == aim[i + j - 1] && dp[i][j - 1]))
{
dp[i][j] = true;
}
}
}
return dp[row][col];
}
/*空间压缩至O(min(m, n))*/
bool isCross2(string s1, string s2, string aim)
{
if (s1.size() < 1 || s2.size() < 1 || aim.size() < 1)
return false;
if (s1.size() + s2.size() != aim.size())
return false;
string longs = s1.size() > s2.size() ? s1 : s2;
string shorts = s1.size() > s2.size() ? s2 : s1;
int more = longs.size();
int less = shorts.size();
vector<bool> dp(less + 1, false);
dp[0] = true;
for (int i = 1; i <= less; i++)
{
if (shorts[i - 1] != aim[i - 1])
break;
dp[i] = true;
}
for (int i = 1; i <= more; i++)
{
dp[0] = dp[0] && longs[i - 1] == aim[i - 1];
for (int j = 1; j <= less; j++)
{
if ((longs[i - 1] == aim[i + j - 1] && dp[j])
|| (shorts[j - 1] == aim[i + j - 1] && dp[j - 1]))
{
dp[j] = true;
}
else
dp[j] = false;
}
}
return dp[less];
}
int main()
{
string s1, s2, aim;
getline(cin, s1);
getline(cin, s2);
getline(cin, aim);
bool res1 = isCross1(s1, s2, aim);
bool res2 = isCross2(s1, s2, aim);
if (res1 && res2)
cout << "Two functions are right!" << endl;
else
cout << "Not same!" << endl;
getchar();
return 0;
}
/*关键点:
1、dp[i][j]代表aim[0..i+j-2]能否被str1[0..i-2]和str2[0..j-1]交错组成,如果可以,那么如果再有str1[i-1]等于aim[i+j-1],说明str1[i-1]又可以作为交错组成aim[0..i+j-1]的最后一个字符。令dp[i][j]=true
2、dp[i][j-1]代表aim[0..i+j-2]能否被str1[0..i-1]和str2[0..j-2]交错组成,如果可以,那么如果再有str2[j-1]等于aim[i+j-1],说明str1[j-1]可以做恶日交错组成aim[0..i+j-1]的最后一个字符。令dp[i][j]=true。
3、如果不满则第一种和第二种情况,令dp[i][j]=false*/