字符串的距离问题

设有字符串X,我们称在X的头尾及中间插入任意多个空格后构成的新字符串为X的扩展串,如字符串X为“abcbcd”,则字符串“abcb_cd”,“_a_bcbcd_”和“abcb_cd_”都是X的扩展串,这里“_”代表空格字符。 如果S1是字符串S的扩展串,T1是字符串T的扩展串,S1与T1具有相同的长度,那么我们定义字符串S1与T1的距离为相应位置上的字符的距离总和,而两个非空格字符的距离定义为它们的ASCII码的差的绝对值,而空格字符与其它任意字符之间的距离为已知的定值VAL,空格字符与空字符的距离为O。在字符串S、T的所有扩展串中,必定存在两个等长的扩展串S1、T1,使得S1与T1之间的距离达到最小,我们将这一距离定义为字符串S、T的距离。 请你写一个程序,求出字符串S、T的距离。

Input
有多组数据,每一组数据第一行为字符串S,第二行为字符串T,S、T均由小写字母组成且长度均不超过2000,第三行为一个整数VAL,1≤VAL≤100,表示空格与其它字符的距离。

Output
每组数据一行包含一个整数,表示要求的字符串S、T的距离。

Sample Input
cmc
snmn
2

Sample Output
10

 

方法一,递归的方法
假设S,T的扩展串S1和T1具有最小距离,那么S1[0]和T1[0]只可能有三种情况:
        Case 1     Case 2       Case 3
S1[0] S[0]       _            S[0]
T1[0]   _          T[0]         T[0]
每种情况下我们只需要求出后继子串的最小距离,再加上当前字符间的距离就能得到整个串的最小距离。因此存在一个递归的过程,可以通过递归的方法解决该问题。
递归过程可描述为:

StrDistance(S[0..SLen], T[0..TLen])= min{
          VAL + StrDistance(S[1..SLEN), T[0..TLEN]),
          VAL + StrDistance(S[0..SLEN], T[1..TLEN]),
          abs(S[0] – T[0]) + StrDitance(S[1..SLEN], T[1..TLEN])
        }
算法实现如下:

#include <math.h>

const int  VAL = 10; 

int StrDistance(char *S, char *T)
{
    int ret, i, temp[3];

    if (S[0] == ‘/0’ && T[0] == ‘/0’)
        return 0;
    if (S[0] == ‘/0’ && T[0] != ‘/0’)
    {
       for (ret = 0, i = 0; T[i] != ‘/0’; i++)
       ret += VAL;
       return ret;  
    }
    if (S[0] != ‘/0’ && T[0] == ‘/0’)
    {
       for (ret = 0, i = 0; S[i] != ‘/0’; i++)
            ret += VAL;
       return ret;  
    }

    temp[0] = StrDistance(S + 1, T) + VAL;
    temp[1] = StrDistance(S, T + 1) + VAL;
    temp[2] = StrDistance(S + 1, T + 1) + abs(S[0] – T[0]);

    ret = min(temp[0], temp[1]);
    ret = min(temp[2], ret);

    return ret;
}

方法二,动态规划的方法
在利用递归方式解决这个问题的时间复杂度过高。进一步分析该问题,我们会发现有许多重复的子问题,例如:

Case 1中的后续子问题(S[1..SLen], T[0,TLen]距离)又有三种情况:
S1[1]   S[1]       _            S[1]
T1[1]   _          T[0]         T[0]
其中这三种情况对应的子问题分别为(S[2..SLen],T[0..TLen]), (S[1..SLen],T[1..TLen]), (S[2..SLen],T[1..TLen])

同理可以分析出Case 2的后续子问题子问题有(S[1..SLen],T[1..TLen]), (S[0..SLen],T[2..TLen]), (S[1..SLen],T[2..TLen])

这里(S[1..SLen],T[1..TLen])便是一个重复的子问题,递归中重复计算子问题造成了复杂都过高。使用动态规划算法,自底向上的计算
各个子问题并利用每次计算的结果,避免重复运算,从而降低算法复杂度。

用dist[i][j]存储S[0..i]和T[0..j]的距离,自底向上的计算过程可以描述为:

dist[i][j] = min{ dist[i-1][j-1] + abs(S[i) – T[j]),
    dist[i-1][j] + VAL,
    dist[i][j-1] + VAL }, 其中i>= 1, j >= 1.

另外 dist[0][0] = 0, dist[0][j] = dist[0][j-1] + VAL, dist[i][0] = dist[j-1][0] + VAL.

算法实现如下:

#include <math.h>

const int  VAL = 10;
const int STR_MAX_LEN = 2000;

int StrDistance(char *S, char *T)
{
   int   i, j, temp[3];
   int   dist[STR_MAX_LEN][STR_MAX_LEN];
   const int  SLen = strlen(S);
   const int  TLen = strlen(T);

   dist[0][0] = 0;
   for (i = 1; i < SLen; i++)
       dist[i][0] = dist[i – 1] + VAL;
   for (i = 1; i < TLen; i++)
       dist[0][i] = dist[i – 1] + VAL;

   for (i = 1; i < SLen; i++)
       for (j = 1; j < TLen; j++)
      {
          temp[0] = dist[i – 1][j – 1] + abs(S[i] – T[j]);
          temp[1] = dist[i – 1][j] + VAL;
          temp[2] = dist[i][j – 1] + VAL;

          dist[i][j] = min(temp[0], temp[1]);
          dist[i][j] = min(temp[2], dist[i][j]);
      }

   return dist[SLen][TLen];
}

点赞