华为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
注意,有些最优的方案可能要做多次的删除和增加操作,不限于两次。

求大神或者华为出这道题目的人给出答案和具体实现的源码,万分感谢!
...全文
981 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
}
C++笔试题 Sony笔试题 几道题目及自做答案 北电 普天C++笔试题 我所收集的intel比试题 面试题 2005年腾讯招聘 微软 微软亚洲技术支持中心面试题目 微创笔试题目(微创,微软在中国的合资公司) Intel笔试面试题目 IBM 社会招聘笔试题 宝洁公司(P&G)面试题目 飞利浦笔试试题 阿尔卡特(中国)的面试题目 Google 戴尔 意法半导体软件试题 Sony笔试题 华为笔试题 华为 华为全套完整试题 慧通 华为面试题 大唐电信 大唐面试试题 网通笔试题 东信笔试题目 中软融鑫笔试题 Delphi笔试题目 EE笔试试题 软件笔试题 Hongkong Bank笔试题 A.T. Keaney笔试题 Shell company笔试题 KPMG笔试题 香港电信笔试题 LORAL的笔试题 维尔VERITAS软件笔试题 百威啤酒(武汉公司) 星巴克 凹凸电子软件笔试题 友立资讯笔试题目 Avant! 微电子EE笔试题 德勤笔试题 扬智(科技)笔试题目 高通笔试题 威盛笔试试题 2003 EE笔试题目 2003 Graphic笔试题目 汉王笔试题 北京信威通信技术股份有限公司面试题 中国国际金融有限公司CICC笔试题 国泰君安笔试题 广东北电面试题目 广州本田笔试题 明基面试问题 网易 广州日报 下面有些题也不错,可以参考 联想笔试题 普天C++笔试题 Sony笔试题 微软亚洲技术中心的面试题 MSRA Interview Written Exam(December 2003,Time:2.5 Hours) 百度笔试题 汉略曾考的测试题目 16道C语言面试题例子 死循环(Infinite loops) 数据声明(Data declarations) 位操作(Bit manipulation) 访问固定的内存位置(Accessing fixed memory locations) 中断(Interrupts) 代码例子(Code examples) 13. 评价下面的代码片断 动态内存分配(Dynamic memory allocation) 群硕笔试 基础题 笔试博朗 - [笔试 职业] C语言面试题大汇总 思科 慧通 雅虎笔试题(字符串操作) C语言最长平台算法 华为3COM C语言题库 将两个无序数组合并为有序链表 上海聚力传媒技术有限公司官方VC笔试题解答

64,648

社区成员

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

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