华为C++岗笔试题:字符串变换最小费用

恋喵大鲤鱼
领域专家: C/C++技术领域
2016-11-02 03:39:31
描述: 给出两个字串A,B。将A字串转化为B字串,转化一共有两种方式:删除连续的n个字符,一次操作费用为2。
增加连续的n个字符(增加的字符是什么由你决定),一次操作费用为n+2。求把A变为B最小费用。
运行时间限制: 无限制
内存限制: 无限制
输入: 第一行输入一个正整数T(1 <= T <= 10),表示有T组测试数据。
对于每组测试数据:
两行字符串A, B(字符串长度不超过2000,字符仅包含小写字母)
输出: 对于每组测试数据,输出一行一个整数,表示最小费用。

样例输入: 2
dsafsadfadf
fdfd
aaaaaaaa
bbbbbbbb
样例输出: 7
12
答案提示: "dsafsadfadf" 变成 "fdfd" 最少的代价的一种方法是:
1. "dsafsadfadf" -> "f" 删除连续的10个,代价2
2. "f" -> "fdfd" 增加连续的3个(”dfd”),代价为3 + 2 = 5
总共的最小代价为2 + 5 = 7,其他方法的代价都不小于7
"aaaaaaaa" 变成 “bbbbbbbb” 最小代价的一种方法是:
1. "aaaaaaaa" 全部删除,代价2
2. 增加8个连续的'b',代价10
总共的最小代价为2 + 10 = 12
注意,有些最优的方案可能要做多次的删除和增加操作,不限于两次。

求大神或者华为出这道题目的人给出答案和具体实现的源码,万分感谢!
...全文
1229 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
恋喵大鲤鱼 2017-07-16
  • 打赏
  • 举报
回复
引用 11 楼 K346K346 的回复:
[quote=引用 10 楼 ID870177103 的回复:] 可以考虑一下什么情况下才会在中间进行删除、增加操作 1。两边相等的情况,但我们已经在递推开始之前跳过了 2。两边不相等,那必先把两边变得相等,最后在修改中间吧,那先改一边,然后递归自己不就好了,这里其实有很大的优化空间,不过不是时间瓶颈的话用对算法就好,要不然逻辑会很乱
根据一楼给出的代码,我觉得不考虑从A的中间部分进行删除和增加操作的原因是,是因为这样做的话,A'的首尾位字符与B'的首尾字符还是不相同,还是需要进行删除或者增加的操作,很明显这样不是最优的,所以抛弃这种做法。 [/quote]感谢,自己总结成了一篇blog,请大家批评指正!http://blog.csdn.net/k346k346/article/details/53013206
Sniper_Pan 2016-11-04
  • 打赏
  • 举报
回复
谢邀,见代码,思路如下: 是否有公共子集? | -> 有 : | -> 比较长短,切取短字符串可能出现的子字符串 | -> 查找长字符串是否包含前面产生的子字符串 | -> 如有,左右删除,并左右添加B中剩余左右部分 | -> 如无,删除所有后添加 | -> 无:全部删除后添加 所有cost存储于结构体数组中,所有过程保存在track中,两字符串完成所有循环后,比较所有cost,找出最小的。 具体请阅读源码,如有其它问题,请回复说明

#include <string>
#include <stdlib.h>
#include <sstream>
#include <iostream>
using namespace std;

#define OPER_ADD_COST 2
#define OPER_ADDR_COST(n) n+2

#define LEN 4
string A[LEN] = { "dsafsadfadf", "aaaaaaaa", "aaaa", "abcdefg" };
string B[LEN] = { "fdfd", "bbbbbbbb", "1aaaa1", "abcdhjk" };

typedef struct 
{
	string subStr;
	string strTrack;
	int nABeginPos;
	int nAEndPos;
	int nBBeginPos;
	int nBEndPos;
	int nFinalCost;
}tagTransCost;

ostringstream osTrack;

//@Sniper n!
int g_arrTagTransCostIdx = -1; 
tagTransCost g_arrTagTransCost[200];

int CalculateCost(bool bContainSub, string A, string B, const string& subStr, tagTransCost& tempTagTransCost)
{
	int nMethod1Cost, nMethod2Cost;
	if (bContainSub)
	{
		if (!subStr.empty())
		{
			string strA = A;
			string strB = B;
			// have equal subStr.

			// Method 1 : remove left and right then add
			// remove equal subStr left and right
			int nMethod1Cost = 0;
			if (tempTagTransCost.nABeginPos == string::npos)
			{
				strA.erase(subStr.length() - 1);
				nMethod1Cost += 2;

				osTrack << "Method1 : erase A from " << subStr.length() - 1
					<< "to end, A become " << strA
					<< ", Method1Cost = " << nMethod1Cost << endl;
			}
			else if (tempTagTransCost.nABeginPos <= tempTagTransCost.nAEndPos
				&& tempTagTransCost.nAEndPos < strA.length())
			{
				if (tempTagTransCost.nAEndPos < strA.length() - 1)
				{
					// Right erase
					strA.erase(tempTagTransCost.nAEndPos + 1);
					nMethod1Cost += 2;
					osTrack << "Method1 : erase A from " << tempTagTransCost.nAEndPos + 1
						<< " to end, A become " << strA
						<< ", Method1Cost = " << nMethod1Cost << endl;
				}

				if (tempTagTransCost.nABeginPos > 0)
				{
					// left erase
					strA.erase(0, tempTagTransCost.nABeginPos);
					nMethod1Cost += 2;
					osTrack << "Method1 : erase A from 0 to " << tempTagTransCost.nABeginPos
						<< ", A become " << strA
						<< ", Method1Cost = " << nMethod1Cost << endl;
				}
			}

			if (subStr.length() != strB.length())
			{
				if (!strB.substr(0, tempTagTransCost.nBBeginPos).empty())
				{
					strA.insert(0, strB.substr(0, tempTagTransCost.nBBeginPos));
					nMethod1Cost += (strB.substr(0, tempTagTransCost.nBBeginPos)).length();
					nMethod1Cost += 2;
					osTrack << "Method1 : insert " << strB.substr(0, tempTagTransCost.nBBeginPos)
						<< " to A at pos 0 , A become " << strA
						<< ", Method1Cost = " << nMethod1Cost << endl;
				}

				if (!strB.substr(tempTagTransCost.nBEndPos + 1, strB.length()).empty())
				{
					strA.insert(strA.length(), strB.substr(tempTagTransCost.nBEndPos + 1, strB.length()));
					nMethod1Cost += (strB.substr(tempTagTransCost.nBEndPos + 1, strB.length())).length();
					nMethod1Cost += 2;
					osTrack << "Method1 : insert " << strB.substr(tempTagTransCost.nBEndPos + 1, strB.length())
						<< " to A at pos " << strA.length() - (strB.substr(tempTagTransCost.nBEndPos + 1, strB.length())).length() << ", A become " << strA
						<< ", Method1Cost = " << nMethod1Cost << endl;
				}
			}

			// Method 2 : clear A[i] and then add new one
			int nMethod2Cost = 0;
			A.clear();
			nMethod2Cost += 2;
			osTrack << "Method2 : clear A , Method2Cost = " << nMethod2Cost << endl;

			// and then add new chars in B[i]
			A = B;
			nMethod2Cost += B.length();
			nMethod2Cost += 2;

			osTrack << "Method2 : copy B to A , Method2Cost = " << nMethod2Cost << endl;

			if (nMethod1Cost == 0)
			{
				tempTagTransCost.nFinalCost = nMethod2Cost;
			}
			else if (nMethod2Cost == 0)
			{
				tempTagTransCost.nABeginPos = nMethod1Cost;
			}
			else
			{
				tempTagTransCost.nFinalCost =
					nMethod1Cost < nMethod2Cost ? nMethod1Cost : nMethod2Cost;
			}
		}
		else
		{
			// have no equal subStr, so remove all first 
			A.clear();
			tempTagTransCost.nFinalCost += 2;

			// and then add new chars in B[i]
			A = B;
			tempTagTransCost.nFinalCost += B.length();
			tempTagTransCost.nFinalCost += 2;
			osTrack << "Have no equal subStr, remove all and then copy, FinalCost = " << tempTagTransCost.nFinalCost;
		}
	}
	else
	{
		// have no equal subStr, so remove all first 
		A.clear();
		tempTagTransCost.nFinalCost += 2;

		// and then add new chars in B[i]
		A = B;
		tempTagTransCost.nFinalCost += B.length();
		tempTagTransCost.nFinalCost += 2;
		osTrack << "Have no equal subStr, remove all and then copy, FinalCost = " << tempTagTransCost.nFinalCost;
	}
	
	return tempTagTransCost.nFinalCost;
}

int FillTransCost(const string& A, const string& B)
{
	string shorterStr;
	string longerStr;
	if (A.length() <= B.length())
	{
		shorterStr = A;
		longerStr = B;
	}
	else if (A.length() > B.length())
	{
		shorterStr = B;
		longerStr = A;
	}

	string subStr;
	for (int i = shorterStr.length(); i > 0; i--)
	{
		for (int j = 0; j <= shorterStr.length() - i; j++)
		{
			subStr = shorterStr.substr(j, i);
			
			{
				++g_arrTagTransCostIdx;
				memset(&g_arrTagTransCost[g_arrTagTransCostIdx], 0, sizeof(tagTransCost));

				g_arrTagTransCost[g_arrTagTransCostIdx].nAEndPos = A.find_last_of(subStr, A.length());
				g_arrTagTransCost[g_arrTagTransCostIdx].nABeginPos = g_arrTagTransCost[g_arrTagTransCostIdx].nAEndPos - subStr.length() + 1;

				g_arrTagTransCost[g_arrTagTransCostIdx].nBBeginPos = B.find(subStr);
				g_arrTagTransCost[g_arrTagTransCostIdx].nBEndPos = g_arrTagTransCost[g_arrTagTransCostIdx].nBBeginPos + subStr.length() - 1;
				
				osTrack << "subStr = " << subStr
					<< ", Before CalculateCost A(" << g_arrTagTransCost[g_arrTagTransCostIdx].nABeginPos
					<< "~" << g_arrTagTransCost[g_arrTagTransCostIdx].nAEndPos
					<< "), B(" << g_arrTagTransCost[g_arrTagTransCostIdx].nBBeginPos
					<< "~" << g_arrTagTransCost[g_arrTagTransCostIdx].nBEndPos
					<< ")" << endl;

				if (string::npos != longerStr.find(subStr))
					CalculateCost(true, A, B, subStr, g_arrTagTransCost[g_arrTagTransCostIdx]);
				else
					CalculateCost(false, A, B, subStr, g_arrTagTransCost[g_arrTagTransCostIdx]);

				g_arrTagTransCost[g_arrTagTransCostIdx].strTrack = osTrack.str();
				osTrack.str("");
			}

			subStr.clear();
		}		
	}
	return g_arrTagTransCostIdx;
}

int main(int argc, char** argv)
{
	int nArrTransCostSize = 0;
	for (int i = 0; i < LEN; i++)
	{
		cout << i <<" : Trans "<< A[i] << " to " << B[i] << " : " << endl;
		nArrTransCostSize = FillTransCost(A[i], B[i]);
		
		int nLeastCostIdx = 0;
		for (int i = 0; i <= g_arrTagTransCostIdx; ++i)
		{
			/*cout << "\ncost : " << g_arrTagTransCost[i].nFinalCost << endl
				 << "track : "<<endl
				 << g_arrTagTransCost[i].strTrack << endl;*/
			if (g_arrTagTransCost[nLeastCostIdx].nFinalCost > g_arrTagTransCost[i].nFinalCost)
			{
				nLeastCostIdx = i;
			}
		}
		cout << "Final Least Cost = " << g_arrTagTransCost[nLeastCostIdx].nFinalCost << "\n"
			<< "Track : \n" 
			<< g_arrTagTransCost[nLeastCostIdx].strTrack << "\n" << endl;
		g_arrTagTransCostIdx = -1;
	}
	return 0;
}
恋喵大鲤鱼 2016-11-03
  • 打赏
  • 举报
回复
引用 10 楼 ID870177103 的回复:
可以考虑一下什么情况下才会在中间进行删除、增加操作 1。两边相等的情况,但我们已经在递推开始之前跳过了 2。两边不相等,那必先把两边变得相等,最后在修改中间吧,那先改一边,然后递归自己不就好了,这里其实有很大的优化空间,不过不是时间瓶颈的话用对算法就好,要不然逻辑会很乱
根据一楼给出的代码,我觉得不考虑从A的中间部分进行删除和增加操作的原因是,是因为这样做的话,A'的首尾位字符与B'的首尾字符还是不相同,还是需要进行删除或者增加的操作,很明显这样不是最优的,所以抛弃这种做法。
ID870177103 2016-11-03
  • 打赏
  • 举报
回复
可以考虑一下什么情况下才会在中间进行删除、增加操作 1。两边相等的情况,但我们已经在递推开始之前跳过了 2。两边不相等,那必先把两边变得相等,最后在修改中间吧,那先改一边,然后递归自己不就好了,这里其实有很大的优化空间,不过不是时间瓶颈的话用对算法就好,要不然逻辑会很乱
赵4老师 2016-11-03
  • 打赏
  • 举报
回复
搜“编辑距离”?
恋喵大鲤鱼 2016-11-03
  • 打赏
  • 举报
回复
引用 7 楼 ID870177103 的回复:
我只是对这题也该兴趣罢了 倒是没想到那一点! 那这样在递推公式中加一段 for (int k = j + 1 ; k <= jr ; k++) tmp = min (tmp ,(k - j) + 2 + func1 (a ,b ,i ,ir ,k ,jr)) ; for (int k = jr - 1 ; k >= j ; k--) tmp = min (tmp ,(jr - k) + 2 + func1 (a ,b ,i ,ir ,j ,k)) ; 应该就可以了 要不你把oj的地址发出来也好
我这边也没有OJ的地址,您这样是增加了从两边增加字符串的操作,但是增加和删除操作不是可以在任何位置吗?
ID870177103 2016-11-02
  • 打赏
  • 举报
回复
我只是对这题也该兴趣罢了
倒是没想到那一点!
那这样在递推公式中加一段
for (int k = j + 1 ; k <= jr ; k++)
tmp = min (tmp ,(k - j) + 2 + func1 (a ,b ,i ,ir ,k ,jr)) ;
for (int k = jr - 1 ; k >= j ; k--)
tmp = min (tmp ,(jr - k) + 2 + func1 (a ,b ,i ,ir ,j ,k)) ;
应该就可以了
要不你把oj的地址发出来也好
恋喵大鲤鱼 2016-11-02
  • 打赏
  • 举报
回复
引用 4 楼 ID870177103 的回复:
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) ; 这是计算数组a的[i,ir]区间的子串转为数组b的[j,jr]区间的子串所需的代价 动态规划和递归法使用相同的递推式,动态规划初始化循环里的代码就是递归法的,这也是我为什么非要写个递归法 不同的是递归法是自顶向下,而动态规划是自底向上 递归法是需要某个结果时就调用自己来计算,动态规划把每次递推的结果保存在ret数组中,因为这里有i,ir,j,jr一个4个变量,所以其实需要一个4维数组,这里用了一个宏代替 每次使用递推式,意味着依赖前面的结果,要保证前面的结果要先被计算出来 在递归法中每次递归i变大,ir变小,j变大,jr变小,所以动态规划的计算顺序要反过来 最后返回的VRET (0 ,la - 1 ,0 ,lb - 1) 其实就是func1入口函数的func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1)
您好,突然发现你的代码有问题,按照您的思路,比如将aaa变为1aaa1的做法是删除aaa,然后增加1aaa1,则变换代价是9,代码的运行结果也是9,但是很明显将aaa变为1aaa1的最优做法是两边各增加一个1,代价是6。请您思考?您是华为公司的吗?
恋喵大鲤鱼 2016-11-02
  • 打赏
  • 举报
回复
引用 3 楼 K346K346 的回复:
[quote=引用 1 楼 ID870177103 的回复:]
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) {
	//ab有相同前缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[i] == b[j]) {
		i++ ;
		j++ ;
	}
	//ab有相同后缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[ir] == b[jr]) {
		ir-- ;
		jr-- ;
	}
	if (i > ir) {
		//如果a是空串,则只需增加操作,代价是b的长度+2
		return (jr + 1 - j) + 2 ;
	} else if (j > jr) {
		//如果b是空串,则只需删除操作,代价是2
		return 2 ;
	}
	//如果ab无公共前缀,则代价是a的所有前后子串转为b的最小值
	//最坏是a的空子串
	int tmp = 2 + (jr + 1 - j) + 2 ;
	//a的非空后子串
	for (int k = i + 1 ; k <= ir ; k++)
		tmp = min (tmp ,2 + func1 (a ,b ,k ,ir ,j ,jr)) ;
	//a的非空前子串
	for (int k = ir - 1 ; k >= i ; k--)
		tmp = min (tmp ,2 + func1 (a ,b ,i ,k ,j ,jr)) ;
	return tmp ;
}

//递归法
int func1 (const string &a ,const string &b) {
	return func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1) ;
}

//动态规划
int func2 (const string &a ,const string &b) {
	const int la = (int) a.length () ;
	const int lb = (int) b.length () ;
	vector<int> ret (la * la * lb * lb) ;
#define VRET(a ,b ,c ,d) (ret[(a) * la * lb * lb + (b) * lb * lb + (c) * lb + (d)])
	for (int ix = la - 1 ; ix >= 0 ; ix--)
		for (int irx = 0 ; irx < la ; irx++)
			for (int jx = lb - 1 ; jx >= 0 ; jx--)
				for (int jrx = 0 ; jrx < lb ; jrx++) {
					int i = ix ;
					int ir = irx ;
					int j = jx ;
					int jr = jrx ;
					while (i <= ir && j <= jr && a[i] == b[j]) {
						i++ ;
						j++ ;
					}
					while (i <= ir && j <= jr && a[ir] == b[jr]) {
						ir-- ;
						jr-- ;
					}
					if (i > ir) {
						VRET (ix ,irx ,jx ,jrx) = (jr + 1 - j) + 2 ;
						continue ;
					} else if (j > jr) {
						VRET (ix ,irx ,jx ,jrx) = 2 ;
						continue ;
					}
					int tmp = 2 + (jr + 1 - j) + 2 ;
					for (int k = i + 1 ; k <= ir ; k++) {
						const auto r0x = ret[k * la * lb * lb + ir * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (k ,ir ,j ,jr)) ;
					}
					for (int k = ir - 1 ; k >= i ; k--) {
						const auto r0x = ret[i * la * lb * lb + k * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (i ,k ,j ,jr)) ;
					}
					VRET (ix ,irx ,jx ,jrx) = tmp ;
					continue ;
				}
	return VRET (0 ,la - 1 ,0 ,lb - 1) ;
#undef VRET
}
谢谢大神的代码,第一个递归法已经测试是正确的,请问动态规划法能够给出一个详细的解释呢?万分感谢,能写成一篇博文就更好啦![/quote]
引用 4 楼 ID870177103 的回复:
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) ; 这是计算数组a的[i,ir]区间的子串转为数组b的[j,jr]区间的子串所需的代价 动态规划和递归法使用相同的递推式,动态规划初始化循环里的代码就是递归法的,这也是我为什么非要写个递归法 不同的是递归法是自顶向下,而动态规划是自底向上 递归法是需要某个结果时就调用自己来计算,动态规划把每次递推的结果保存在ret数组中,因为这里有i,ir,j,jr一个4个变量,所以其实需要一个4维数组,这里用了一个宏代替 每次使用递推式,意味着依赖前面的结果,要保证前面的结果要先被计算出来 在递归法中每次递归i变大,ir变小,j变大,jr变小,所以动态规划的计算顺序要反过来 最后返回的VRET (0 ,la - 1 ,0 ,lb - 1) 其实就是func1入口函数的func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1)
大概明白了你的意思,但是你的做法具体是在左边起删连续的字符,或者右起删连续的字符,但是题目不是说可以从任意位置删除吗?
ID870177103 2016-11-02
  • 打赏
  • 举报
回复
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) ; 这是计算数组a的[i,ir]区间的子串转为数组b的[j,jr]区间的子串所需的代价 动态规划和递归法使用相同的递推式,动态规划初始化循环里的代码就是递归法的,这也是我为什么非要写个递归法 不同的是递归法是自顶向下,而动态规划是自底向上 递归法是需要某个结果时就调用自己来计算,动态规划把每次递推的结果保存在ret数组中,因为这里有i,ir,j,jr一个4个变量,所以其实需要一个4维数组,这里用了一个宏代替 每次使用递推式,意味着依赖前面的结果,要保证前面的结果要先被计算出来 在递归法中每次递归i变大,ir变小,j变大,jr变小,所以动态规划的计算顺序要反过来 最后返回的VRET (0 ,la - 1 ,0 ,lb - 1) 其实就是func1入口函数的func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1)
恋喵大鲤鱼 2016-11-02
  • 打赏
  • 举报
回复
引用 1 楼 ID870177103 的回复:
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) {
	//ab有相同前缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[i] == b[j]) {
		i++ ;
		j++ ;
	}
	//ab有相同后缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[ir] == b[jr]) {
		ir-- ;
		jr-- ;
	}
	if (i > ir) {
		//如果a是空串,则只需增加操作,代价是b的长度+2
		return (jr + 1 - j) + 2 ;
	} else if (j > jr) {
		//如果b是空串,则只需删除操作,代价是2
		return 2 ;
	}
	//如果ab无公共前缀,则代价是a的所有前后子串转为b的最小值
	//最坏是a的空子串
	int tmp = 2 + (jr + 1 - j) + 2 ;
	//a的非空后子串
	for (int k = i + 1 ; k <= ir ; k++)
		tmp = min (tmp ,2 + func1 (a ,b ,k ,ir ,j ,jr)) ;
	//a的非空前子串
	for (int k = ir - 1 ; k >= i ; k--)
		tmp = min (tmp ,2 + func1 (a ,b ,i ,k ,j ,jr)) ;
	return tmp ;
}

//递归法
int func1 (const string &a ,const string &b) {
	return func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1) ;
}

//动态规划
int func2 (const string &a ,const string &b) {
	const int la = (int) a.length () ;
	const int lb = (int) b.length () ;
	vector<int> ret (la * la * lb * lb) ;
#define VRET(a ,b ,c ,d) (ret[(a) * la * lb * lb + (b) * lb * lb + (c) * lb + (d)])
	for (int ix = la - 1 ; ix >= 0 ; ix--)
		for (int irx = 0 ; irx < la ; irx++)
			for (int jx = lb - 1 ; jx >= 0 ; jx--)
				for (int jrx = 0 ; jrx < lb ; jrx++) {
					int i = ix ;
					int ir = irx ;
					int j = jx ;
					int jr = jrx ;
					while (i <= ir && j <= jr && a[i] == b[j]) {
						i++ ;
						j++ ;
					}
					while (i <= ir && j <= jr && a[ir] == b[jr]) {
						ir-- ;
						jr-- ;
					}
					if (i > ir) {
						VRET (ix ,irx ,jx ,jrx) = (jr + 1 - j) + 2 ;
						continue ;
					} else if (j > jr) {
						VRET (ix ,irx ,jx ,jrx) = 2 ;
						continue ;
					}
					int tmp = 2 + (jr + 1 - j) + 2 ;
					for (int k = i + 1 ; k <= ir ; k++) {
						const auto r0x = ret[k * la * lb * lb + ir * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (k ,ir ,j ,jr)) ;
					}
					for (int k = ir - 1 ; k >= i ; k--) {
						const auto r0x = ret[i * la * lb * lb + k * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (i ,k ,j ,jr)) ;
					}
					VRET (ix ,irx ,jx ,jrx) = tmp ;
					continue ;
				}
	return VRET (0 ,la - 1 ,0 ,lb - 1) ;
#undef VRET
}
谢谢大神的代码,第一个递归法已经测试是正确的,请问动态规划法能够给出一个详细的解释呢?万分感谢,能写成一篇博文就更好啦!
恋喵大鲤鱼 2016-11-02
  • 打赏
  • 举报
回复
引用 1 楼 ID870177103 的回复:
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) {
	//ab有相同前缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[i] == b[j]) {
		i++ ;
		j++ ;
	}
	//ab有相同后缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[ir] == b[jr]) {
		ir-- ;
		jr-- ;
	}
	if (i > ir) {
		//如果a是空串,则只需增加操作,代价是b的长度+2
		return (jr + 1 - j) + 2 ;
	} else if (j > jr) {
		//如果b是空串,则只需删除操作,代价是2
		return 2 ;
	}
	//如果ab无公共前缀,则代价是a的所有前后子串转为b的最小值
	//最坏是a的空子串
	int tmp = 2 + (jr + 1 - j) + 2 ;
	//a的非空后子串
	for (int k = i + 1 ; k <= ir ; k++)
		tmp = min (tmp ,2 + func1 (a ,b ,k ,ir ,j ,jr)) ;
	//a的非空前子串
	for (int k = ir - 1 ; k >= i ; k--)
		tmp = min (tmp ,2 + func1 (a ,b ,i ,k ,j ,jr)) ;
	return tmp ;
}

//递归法
int func1 (const string &a ,const string &b) {
	return func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1) ;
}

//动态规划
int func2 (const string &a ,const string &b) {
	const int la = (int) a.length () ;
	const int lb = (int) b.length () ;
	vector<int> ret (la * la * lb * lb) ;
#define VRET(a ,b ,c ,d) (ret[(a) * la * lb * lb + (b) * lb * lb + (c) * lb + (d)])
	for (int ix = la - 1 ; ix >= 0 ; ix--)
		for (int irx = 0 ; irx < la ; irx++)
			for (int jx = lb - 1 ; jx >= 0 ; jx--)
				for (int jrx = 0 ; jrx < lb ; jrx++) {
					int i = ix ;
					int ir = irx ;
					int j = jx ;
					int jr = jrx ;
					while (i <= ir && j <= jr && a[i] == b[j]) {
						i++ ;
						j++ ;
					}
					while (i <= ir && j <= jr && a[ir] == b[jr]) {
						ir-- ;
						jr-- ;
					}
					if (i > ir) {
						VRET (ix ,irx ,jx ,jrx) = (jr + 1 - j) + 2 ;
						continue ;
					} else if (j > jr) {
						VRET (ix ,irx ,jx ,jrx) = 2 ;
						continue ;
					}
					int tmp = 2 + (jr + 1 - j) + 2 ;
					for (int k = i + 1 ; k <= ir ; k++) {
						const auto r0x = ret[k * la * lb * lb + ir * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (k ,ir ,j ,jr)) ;
					}
					for (int k = ir - 1 ; k >= i ; k--) {
						const auto r0x = ret[i * la * lb * lb + k * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (i ,k ,j ,jr)) ;
					}
					VRET (ix ,irx ,jx ,jrx) = tmp ;
					continue ;
				}
	return VRET (0 ,la - 1 ,0 ,lb - 1) ;
#undef VRET
}
引用 1 楼 ID870177103 的回复:
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) {
	//ab有相同前缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[i] == b[j]) {
		i++ ;
		j++ ;
	}
	//ab有相同后缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[ir] == b[jr]) {
		ir-- ;
		jr-- ;
	}
	if (i > ir) {
		//如果a是空串,则只需增加操作,代价是b的长度+2
		return (jr + 1 - j) + 2 ;
	} else if (j > jr) {
		//如果b是空串,则只需删除操作,代价是2
		return 2 ;
	}
	//如果ab无公共前缀,则代价是a的所有前后子串转为b的最小值
	//最坏是a的空子串
	int tmp = 2 + (jr + 1 - j) + 2 ;
	//a的非空后子串
	for (int k = i + 1 ; k <= ir ; k++)
		tmp = min (tmp ,2 + func1 (a ,b ,k ,ir ,j ,jr)) ;
	//a的非空前子串
	for (int k = ir - 1 ; k >= i ; k--)
		tmp = min (tmp ,2 + func1 (a ,b ,i ,k ,j ,jr)) ;
	return tmp ;
}

//递归法
int func1 (const string &a ,const string &b) {
	return func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1) ;
}

//动态规划
int func2 (const string &a ,const string &b) {
	const int la = (int) a.length () ;
	const int lb = (int) b.length () ;
	vector<int> ret (la * la * lb * lb) ;
#define VRET(a ,b ,c ,d) (ret[(a) * la * lb * lb + (b) * lb * lb + (c) * lb + (d)])
	for (int ix = la - 1 ; ix >= 0 ; ix--)
		for (int irx = 0 ; irx < la ; irx++)
			for (int jx = lb - 1 ; jx >= 0 ; jx--)
				for (int jrx = 0 ; jrx < lb ; jrx++) {
					int i = ix ;
					int ir = irx ;
					int j = jx ;
					int jr = jrx ;
					while (i <= ir && j <= jr && a[i] == b[j]) {
						i++ ;
						j++ ;
					}
					while (i <= ir && j <= jr && a[ir] == b[jr]) {
						ir-- ;
						jr-- ;
					}
					if (i > ir) {
						VRET (ix ,irx ,jx ,jrx) = (jr + 1 - j) + 2 ;
						continue ;
					} else if (j > jr) {
						VRET (ix ,irx ,jx ,jrx) = 2 ;
						continue ;
					}
					int tmp = 2 + (jr + 1 - j) + 2 ;
					for (int k = i + 1 ; k <= ir ; k++) {
						const auto r0x = ret[k * la * lb * lb + ir * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (k ,ir ,j ,jr)) ;
					}
					for (int k = ir - 1 ; k >= i ; k--) {
						const auto r0x = ret[i * la * lb * lb + k * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (i ,k ,j ,jr)) ;
					}
					VRET (ix ,irx ,jx ,jrx) = tmp ;
					continue ;
				}
	return VRET (0 ,la - 1 ,0 ,lb - 1) ;
#undef VRET
}
您好,请问第一种方法各个参数分别是什么意思呢?
ID870177103 2016-11-02
  • 打赏
  • 举报
回复
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) {
	//ab有相同前缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[i] == b[j]) {
		i++ ;
		j++ ;
	}
	//ab有相同后缀,则去除掉也不影响
	while (i <= ir && j <= jr && a[ir] == b[jr]) {
		ir-- ;
		jr-- ;
	}
	if (i > ir) {
		//如果a是空串,则只需增加操作,代价是b的长度+2
		return (jr + 1 - j) + 2 ;
	} else if (j > jr) {
		//如果b是空串,则只需删除操作,代价是2
		return 2 ;
	}
	//如果ab无公共前缀,则代价是a的所有前后子串转为b的最小值
	//最坏是a的空子串
	int tmp = 2 + (jr + 1 - j) + 2 ;
	//a的非空后子串
	for (int k = i + 1 ; k <= ir ; k++)
		tmp = min (tmp ,2 + func1 (a ,b ,k ,ir ,j ,jr)) ;
	//a的非空前子串
	for (int k = ir - 1 ; k >= i ; k--)
		tmp = min (tmp ,2 + func1 (a ,b ,i ,k ,j ,jr)) ;
	return tmp ;
}

//递归法
int func1 (const string &a ,const string &b) {
	return func1 (&a[0] ,&b[0] ,0 ,(int) a.length () - 1 ,0 ,(int) b.length () - 1) ;
}

//动态规划
int func2 (const string &a ,const string &b) {
	const int la = (int) a.length () ;
	const int lb = (int) b.length () ;
	vector<int> ret (la * la * lb * lb) ;
#define VRET(a ,b ,c ,d) (ret[(a) * la * lb * lb + (b) * lb * lb + (c) * lb + (d)])
	for (int ix = la - 1 ; ix >= 0 ; ix--)
		for (int irx = 0 ; irx < la ; irx++)
			for (int jx = lb - 1 ; jx >= 0 ; jx--)
				for (int jrx = 0 ; jrx < lb ; jrx++) {
					int i = ix ;
					int ir = irx ;
					int j = jx ;
					int jr = jrx ;
					while (i <= ir && j <= jr && a[i] == b[j]) {
						i++ ;
						j++ ;
					}
					while (i <= ir && j <= jr && a[ir] == b[jr]) {
						ir-- ;
						jr-- ;
					}
					if (i > ir) {
						VRET (ix ,irx ,jx ,jrx) = (jr + 1 - j) + 2 ;
						continue ;
					} else if (j > jr) {
						VRET (ix ,irx ,jx ,jrx) = 2 ;
						continue ;
					}
					int tmp = 2 + (jr + 1 - j) + 2 ;
					for (int k = i + 1 ; k <= ir ; k++) {
						const auto r0x = ret[k * la * lb * lb + ir * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (k ,ir ,j ,jr)) ;
					}
					for (int k = ir - 1 ; k >= i ; k--) {
						const auto r0x = ret[i * la * lb * lb + k * lb * lb + j * lb + jr] ;
						tmp = min (tmp ,2 + VRET (i ,k ,j ,jr)) ;
					}
					VRET (ix ,irx ,jx ,jrx) = tmp ;
					continue ;
				}
	return VRET (0 ,la - 1 ,0 ,lb - 1) ;
#undef VRET
}

65,213

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧