• 求两个字符串的不连续的公共字串


    无论题目怎么样,始终有一个宗旨,那就是一步一步接近结果,最后你会发现一个最终最优化的解题思路,如果你直接看最优解题思路,会比较突兀,理解起来不是很好,一步一步想,会感觉水到渠成,最后你会发现任何已有的算法都基于一个很朴素的想法。

    问题描述

    给定两个字符串,求出字符串中最长匹配的公共字符串,而且可以不用相连,比如"abcdef"和"abfce",我们可以找到“abce”是最长的公共字符串

    问题分解

    其实上述的问题,可以分解成两个子问题

    (1)首先,找出相连的公共子字符串,比如"abcdef"和"abfce",结果就是"ab"

    (2)其次,再考虑这个问题,即不相连的公共字符串

    对第一个子问题的分析

     一拿到这个问题,最暴力的方法就是穷举,再一一比较字符串。

    再想想,我们就可以发现字符串的比较,其实就是比较字符。为了避免重复的比较,我们用一个二维数组记录之前的比较结果。

    下面以"abfcd", "abcdef"为例,加以说明。

    [ab, ab]而言,对于的是2,也就是说最长公共字串是2.

    [abf, ab]而言,在3*2的矩阵中,最大的数字也是2,那就意味着最长公共长度也是2.

    比较字符是否相同,若两字符相同,则对于的数字是斜对角的数字加一。

    至于结果的打印,我们可以通过记录下最大的长度,再找出所有最大的长度,就可以打印所有的结果了。

    这样就把穷举简化成时间复杂度为o(n*m)的问题,其实这就是动态规划,其本质就是用空间记录之前的比较结果,以此来达到优化时间复杂度的目的。

    该算法空间负责度为o(n*m),我们还可以进行再一步优化为o(n)的时间复杂度,其思路跟背包问题相似,至于是逆序,顺序都可以,但是为了打印所有结果的话,必须得用逆序实现。

     1 //find the successive common substring
     2 #include <stdio.h>
     3 #include <iostream>
     4 using namespace std;
     5 #define N 100
     6 int a[N + 5][N + 5];
     7 char s1[N + 5] = "abfcd";
     8 char s2[N + 5] = "abcdef";
     9 void main()
    10 {
    11     int i, j, k, len1, len2, max, count;
    12     //---------------o(n * n)
    13     max = 0;
    14     for (i = 0; s1[i] != ''; i++)
    15     {
    16         for (j = 0; s2[j] != ''; j++)
    17         {
    18             if (s1[i] == s2[j])
    19             {
    20                 a[i + 1][j + 1] = a[i][j] + 1;
    21                 if (max < a[i + 1][j + 1])
    22                     max = a[i + 1][j + 1];
    23             }
    24             else
    25             {
    26                 a[i + 1][j + 1] = 0;
    27             }
    28         }
    29     }
    30     //--------for check
    31     /**/
    32     for (i = 0; s1[i] != ''; i++)
    33     {
    34         for (j = 0; s2[j] != ''; j++)
    35         {
    36             printf("%d ", a[i + 1][j + 1]);
    37         }
    38         puts("");
    39     }
    40     
    41     //----------print the result
    42     count = 0;
    43     for (i = 0; s1[i] != ''; i++)
    44     {
    45         for (j = 0; s2[j] != ''; j++)
    46         {
    47             if (a[i + 1][j + 1] == max)
    48             {
    49                 for (k = i - max + 1; k <= i; k++)
    50                     printf("%c", s1[k]);
    51                 count++;
    52                 puts("");
    53             }
    54             
    55         }
    56     }
    57     printf("total : %d
    ", count);
    58     system("pause");
    59 }

     对第二个子问题的分析

    了解了第一问的解题思路,稍加修改,就可以解决第二问的问题。

    我们第一问是若相同则斜对角加一,若不同,则置为0,这里是若相同,则斜对角加一,若不同则取反斜对角中较大的数。

    我们可以这么理解,f(n, m)表示的是长度为n和长度为m的字符串的公共字串的长度,则if str1[n] == str2[m], f(n, m) = f(n - 1, m - 1) + 1; if str1[n] != str2[m], f(n, m) = max(f(n - 1, m), f(n, m - 1))

    好了,那接下来的问题就是如何打印的问题,仅仅是以上的数字,我们发现要想打印还得花一些功夫。所以,我们在上面的运算过程中,还记录了另一个数据,就是方向,就是说当前这个值时从斜对角来的,dir就是1;若是从左边来的,dir就是2;若是从上面来的,dir就是3。有了这个之后,打印就轻而易举了。

     1 #include <stdio.h>
     2 #include <iostream>
     3 using namespace std;
     4 #define N 100
     5 struct
     6 {
     7     int len;
     8     int dir;
     9 }a[N + 5][N + 5];
    10 char s1[N + 5] = "cdbef";
    11 char s2[N + 5] = "abcdef";
    12 char res[N + 5];
    13 void main()
    14 {
    15     int i, j, k, len1, len2, count;    
    16     //---------------o(n * n)
    17     for (i = 0; s1[i] != ''; i++)
    18     {
    19         for (j = 0; s2[j] != ''; j++)
    20         {
    21             if (s1[i] == s2[j])
    22             {
    23                 a[i + 1][j + 1].len = a[i][j].len + 1;
    24                 a[i + 1][j + 1].dir = 1;
    25             }
    26             else
    27             {
    28                 if (a[i][j + 1].len > a[i + 1][j].len)
    29                 {
    30                     a[i + 1][j + 1].len = a[i][j + 1].len;
    31                     a[i + 1][j + 1].dir = 3;
    32                 }
    33                 else
    34                 {
    35                     a[i + 1][j + 1].len = a[i + 1][j].len;
    36                     a[i + 1][j + 1].dir = 2;
    37                 }
    38             }
    39         }
    40     }
    41     //--------for check
    42     /**/
    43     for (i = 0; s1[i] != ''; i++)
    44     {
    45         for (j = 0; s2[j] != ''; j++)
    46         {
    47             printf("%d ", a[i + 1][j + 1].len);
    48         }
    49         puts("");
    50     }
    51     puts("");
    52     for (i = 0; s1[i] != ''; i++)
    53     {
    54         for (j = 0; s2[j] != ''; j++)
    55         {
    56             printf("%d ", a[i + 1][j + 1].dir);
    57         }
    58         puts("");
    59     }
    60     
    61     //----------print the result
    62     len1 = i;
    63     len2 = j;
    64     int len = a[len1][len2].len;
    65     for (k = len - 1; k >= 0;)
    66     {
    67         if (a[i][j].dir == 1)
    68         {
    69             res[k] = s1[i - 1];
    70             k--;
    71             i--;
    72             j--;
    73         }
    74         else if (a[i][j].dir == 2)
    75         {
    76             j--;
    77         }
    78         else if (a[i][j].dir == 3)
    79         {
    80             i--;
    81         }
    82     }
    83 
    84     for (k = 0; k < len; k++)
    85     {
    86         printf("%c", res[k]);
    87     }
    88     puts("");
    89     system("pause");
    90 }
  • 相关阅读:
    wordcloud库基本介绍和使用方法
    文本词频同意问题分析
    集合
    操作系统
    操作系统的发展史
    基础练习
    random库的使用
    【量化】五日均价策略
    【量化】多只股票策略
    【量化】指数数据
  • 原文地址:https://www.cnblogs.com/chuanlong/p/3281164.html
Copyright © 2020-2023  润新知