POJ 4007:计算字符串距离

总时间限制: 
1000ms 
内存限制: 
65536kB
描述
对于两个不同的字符串,我们有一套操作方法来把他们变得相同,具体方法为:
  1. 修改一个字符(如把“a”替换为“b”)
  2. 删除一个字符(如把“traveling”变为“travelng”)

比如对于“abcdefg”和“abcdef”两个字符串来说,我们认为可以通过增加/减少一个“g”的方式来达到目的。无论增加还是减少“g”,我们都仅仅需要一次操作。我们把这个操作所需要的次数定义为两个字符串的距离。 
给定任意两个字符串,写出一个算法来计算出他们的距离。
输入
第一行有一个整数n。表示测试数据的组数,
接下来共n行,每行两个字符串,用空格隔开。表示要计算距离的两个字符串
字符串长度不超过1000。
输出
针对每一组测试数据输出一个整数,值为两个字符串的距离。
样例输入
3
abcdefg  abcdef
ab ab
mnklj jlknm
样例输出
1
0
4

这道题的时间限制是1秒,但处理的字符串长度可能达到1000,所以如果使用递归的话,则必然超时。因此,可采用动态规划的方法,构造一个二维数组,数组的“长”和“宽”分别是两个字符串的长度。

以题中的一个例子举例,字符串a=“jlknm”,b=“mnklj”,则可构造一个6×6的二维数组array,并将二维数组的第一行和第一列初始化为如下,因为一个空字符串和任意一个字符串的距离始终为该字符串的长度。


用两层for循环遍历两个字符串a、b(i和j从1开始取值),对比每一个字符a[i-1]和b[j-1],若两个字符相等,即a[i-1] == b[j-1],则不增加距离,因此对应的数组array[i][j]=array[i-1][j-1];若两个字符不相等,则可删除a[i-1]这个字符、删除b[j-1]这个字符,或修改a[i-1]使它与b[i-1]相等。array[i][j]取这三种方法的最小值。选择修改,则array[i][j] = 1 + array[i-1][j-1],若选择删除a[i-1],则array[i][j] = 1 + array[i-1][j],若选择删除b[i-1],则array[i][j] = 1 + array[i][j-1]。运用这个方法,可将二维数组填满,如下图所示。


最后,取这个数组的最后一个元素4,即为答案。


最终AC的代码如下:

#include 
#include 
using namespace std;

int m_min(int a, int b, int c){
	if (a <= b && a <= c)
		return a;
	else if (b <= a && b <= c)
		return b;
	else
		return c;
		
}

int count_distance(string a, string b){
	int na = a.size();
	int nb = b.size();
	
	// 分配二维数组
	int **array = new int*[na + 1];
	for (int i = 0; i <= na; ++i){
		array[i] = new int[nb + 1];
	}
	
	// 初始化二维数组的第一行和第一列 
	for (int i = 0; i <= nb; ++i){
		array[0][i] = i;
	}
 	for (int i = 0; i <= na; ++i){
	 	array[i][0] = i;
 	}
	
	for (int i = 1; i <= na; ++i){
		for (int j = 1; j <= nb; ++j){
			if (a[i - 1] == b[j - 1])
				array[i][j] = array[i-1][j-1];
			else
				array[i][j] = m_min(1 + array[i-1][j-1], 1 + array[i-1][j], 1 + array[i][j-1]);
		}
	}
	
	int m_distance = array[na][nb];
	
	// 释放二维数组 
	for (int i = 0; i <= na; ++i){
		delete[] array[i];
	}
	delete[] array;
	
	return m_distance;
}

int main(){
	int n = 0;
	string a, b;
	cin >> n;
	while(n--){
		cin >> a >> b;
		cout << count_distance(a, b) << endl;
	}
}

做完这题,本学期也结束了,收拾收拾东西,明天去深圳出差,完事了耍起回家~


----------------------------------------------------------------------分割线----------------------------------------------------------------------------

附上错误经历,以提醒自己。

下面这个解法思路比较清晰,但是超时。。

#include 
#include 
using namespace std;

int count_distance(string a, string b){
	if (a == "")
		return b.size();
	if (b == "")
		return a.size();
	if (a[0] == b[0])
		return count_distance(string(a.begin()+1, a.end()), string(b.begin()+1, b.end()));
			
	return min(1 + count_distance(string(a.begin()+1, a.end()), string(b.begin()+1, b.end())),// 修改 
		min(1 + count_distance(a, string(b.begin()+1, b.end())), 1 + count_distance(string(a.begin()+1, a.end()), b))); // 删除 
}

int main(){
	int n = 0;
	string a, b;
	cin >> n;
	while(n--){
		cin >> a >> b;
		cout << count_distance(a, b) << endl;
	}
}

再附一个脑洞大开的解法,只能计算较短字符串,计算长字符串的距离时,会严重超时。

#include 
#include 
using namespace std;

int m_distance;

int count_distance(string a, string b, int distance){
	if (distance >= m_distance)
		return m_distance;
	
	// 若两个子串相等,则不再增加距离 
	if (a == b){
		m_distance = distance;
		return distance;	
	}
	
	// 两个字符串长度相等,则应修改位置 
	if (a.size() == b.size()){
		for (string::size_type i = 0; i < a.size(); ++i){
			if (a[i] != b[i])
				++distance;
		}
		if (distance < m_distance)
			m_distance = distance;
		return distance;
	}	
	
	// 删除一个字符,字符位置是i 
	for (string::size_type i = 0; i < a.size(); ++i){
		if (distance + 1 >= m_distance)
			return m_distance;
		string temp_a = a.substr(0, i) + a.substr(i+1);
		count_distance(temp_a, b, distance + 1);
	}
}

int main(){
	int n = 0;
	string a, b;
	cin >> n;
	while(n--){
		m_distance = 1001;
		cin >> a >> b;
		a.size() >= b.size() ? count_distance(a, b, 0) : count_distance(b, a, 0);
		cout << m_distance << endl;
	}
}







你可能感兴趣的:(poj,c/c++,c++,poj)