字符串的距离,编程之美上给了一个最简答的递归的做法,显示时间复杂度很高,要写的更好一些,就必须是DP,C++代码如下:
题目如下:
许多程序会大量使用字符串。对于不同的字符串,我们希望能够有办法判断其相似程度。我们定义了一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:
1. 修改一个字符 (如 把“a”替换为“b”)。
2. 增加一个字符 (如把“abdd”变为“aebdd”)。
3. 删除一个字符(如把“travelling”变为“traveling”)。
比如,对于“abcdefg”和“abcdef”两个字符串来说,我们认为可以通过增加/减少一个“g”的方式来达到目的。上面的两种方案,都仅需要一次操作。把这个操作所需要的次数定义为两个字符串的距离,而相似度等于“距离+1”的倒数。也就是说,“abcdefg”和“abcdef”的距离为1,相似度为1 / 2 = 0.5给定任意两个字符串,你是否能写出一个算法来计算出它们的相似度呢?
分析与解法:
不难看出,两个字符串的距离肯定不超过它们的长度之和(我们可以通过删除操作把两个串都转化为空串)。虽然这个结论对结果没有帮助,但至少可以知道,任意两个字符串的距离都是有限的。
我们还是应该集中考虑如何才能把这个问题转化成规模较小的同样的问题。如果有两个串A=xabcdae 和B=xfdfa,它们的第一个字符是相同的,只要计算A[2, …, 7] = abcdae和B[2, …, 5] = fdfa 的距离就可以了。但是如果两个串的第一个字符不相同,那么可以进行如下的操作(lenA 和lenB 分别是A 串和B 串的长度):
1. 删除A串的第一个字符,然后计算A[2, …, lenA]和B[1, …, lenB]的距离。
2. 删除B串的第一个字符,然后计算A[1, …, lenA]和B[2, …, lenB]的距离。
3. 修改A串的第一个字符为B串的第一个字符,然后计算A[2, …, lenA]和B[2, …,lenB]的距离。
4. 修改B串的第一个字符为A串的第一个字符,然后计算A[2, …, lenA]和B[2, …,lenB]的距离。
5. 增加B串的第一个字符到A串的第一个字符之前,然后计算A[1, …, lenA]和B[2, …, lenB]的距离。
6. 增加A串的第一个字符到B串的第一个字符之前,然后计算A[2, …, lenA]和B[1, …, lenB]的距离。
在这个题目中,我们并不在乎两个字符串变得相等之后的字符串是怎样的。所以,
可以将上面6 个操作合并为:
1. 一步操作之后,再将A[2, …, lenA]和B[1, …, lenB]变成相同字符串。
2. 一步操作之后,再将A[1, …, lenA]和B[2, …, lenB]变成相同字符串。
3. 一步操作之后,再将A[2, …, lenA]和B[2, …, lenB]变成相同字符串。
动态规划代码如下:(递归算法比较简单,免了)
#include<iostream>
#include<string>
using namespace std;
int main()
{
//just ignore the string init
string str1="tomcat";
string str2="jtocftt";
cin>>str1;
cin>>str2;
//the distance of them should be 4
int row=str2.length();
int col=str1.length();
//define the 2dimension array
int **arr=new int*[row+1];
for(int i=0;i<=row;i++)
arr[i]=new int[col+1];
//init the outindex value
for(int t=0;t<=row;t++)
arr[t][col]=row-t;
for(int t=0;t<=col;t++)
arr[row][t]=col-t;
//begin to dp
int m1,m2,m3;
int min_t,min;
for(int i=row-1;i>=0;i--)
for(int j=col-1;j>=0;j--)
{
if( str2.at(i)==str1.at(j) )
{
arr[i][j]=arr[i+1][j+1];
continue;
}
m1=arr[i+1][j+1];
m2=arr[i+1][j];
m3=arr[i][j+1];
min_t=m1<m2?m1:m2;
min=min_t<m3?min_t:m3;
arr[i][j]=min+1;
}
for(int i=0;i<=row;i++)
{
for(int j=0;j<=col;j++)
{
cout<<" "<<arr[i][j];
}
cout<<endl;
}
return 0;
}
LCS:这是算法导论的例题 ,实现了一下
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str1="abbcda";
string str2="daabcca";
str1="tomcat";
str2="oomtcatomccffatqb";
//the answer should be "abca"
int row=str2.length();
int col=str1.length();
//init the arr and the value,both of them are 2-dim
int **arr=new int*[row+1];
for(int i=0;i<=row;i++)
arr[i]=new int[col+1];
for(int t=0;t<=row;t++)
arr[t][col]=0;
for(int t=0;t<=col;t++)
arr[row][t]=0;
char **value=new char*[row+1];
for(int i=0;i<=row;i++)
value[i]=new char[col+1];
//begin to DP
int m1,m2;
for(int i=row-1;i>=0;i--)
for(int j=col-1;j>=0;j--)
{
if(str2.at(i)==str1.at(j))
{
arr[i][j]=arr[i+1][j+1]+1;
value[i][j]='X';//the down and right
continue;
}
m1=arr[i][j+1];
m2=arr[i+1][j];
if(m1>m2)
{
arr[i][j]=m1;
value[i][j]='R';//right
}
else
{
arr[i][j]=m2;
value[i][j]='D';//down
}
}
//find the LCS according arr and value
int i=0;int j=0;
while(i!=row && j!=col)
{
if(value[i][j]=='X')
{
cout<<" "<<str2.at(i);
i++;j++;
}
else if(value[i][j]=='R')
{
j++;
}
else
{
i++;
}
}
cout<<endl;
return 0;
}
最长公共子串LCS2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
String str1 = "welcoabcdefghijklme to the furture";
String str2 = "He is cr lovely capturabcdefghijkle";
List<List<int>> result = new List<List<int>>();
int big=0, x=0, y=0;
for (int i = 0; i < str1.Length; i++)
{
result.Add(new List<int>(new int[str2.Length]));
for (int j = 0; j < str2.Length; j++)
{
if (str1[i] == str2[j])
{
if (i > 0 && j > 0)
{
result[i][j] = result[i - 1][j - 1] + 1;
if (result[i][j] > big)
{
x = i;
y = j;
big = result[i][j];
}
}
else
{
result[i][j] = 1;
}
}
else
{
result[i][j] = 0;
}
}
}
Console.WriteLine(str1.Substring(x - big + 1, big));
}
}
}