学校算法作业 (六)——— 子串

  哎,这次ACM的题有些复杂,毕竟字符串题还是有不小难度的,我们来看一下题.(评测机没开,没图了,将就着看看吧)

问题描述

现在有一些由英文字符组成的大小写敏感的字符串,
你的任务是找到一个最长的字符串x,
使得对于已经给出的字符串中的任意一个y,x或者是y的子串,
或者x中的字符反序之后得到的新字符串是y的子串。

输入数据

输入的第一行是一个整数t (1 <= t <= 10),t表示测试数据的数目。
对于每一组测试数据,第一行是一个整数n (1 <= n <= 100),
表示已经给出n个字符串。接下来n行,每行给出一个长度在1和100之间的字符串。

输出要求

对于每一组测试数据,输出一行,给出题目中要求的字符串x的长度。

输入样例

2
3
ABCD
BCDFF
BRCD
2
rose
orchid

输出样例

2
2

  考察的属于最长子序列的查找,这道题没什么特别恶心人的地方,暴力就好了。或者优化一下匹配函数。下面我说一下思路。

  1、找出长度最小的字串。(最长也不会最小的输入字串长)

  2、反转最短字串

  3、找出最大相同字串

思路大概就这三步,实现起来,还是有点意思的。

       第一步,可以在输入的时候完成,设置一个min变量;

       第二步,可以写个函数实现,也可以直接调用 strrev (自己查怎么用);

       第三步,先选定想用的模式匹配函数,(kmp或者Strstr或者其它)然后,用两重for循环控制字符长度以及正反向递减,再嵌入一层循环控制字符串与模式串之间的比较,完事儿。

讲的有点抽象,为了先打一个大的框架,这也是我的编程思维——自顶向下,逐步细化。

      下面贴一下第三步代码,略微讲一下,没啥技巧,纯暴力

 

 1 /**
 2  * @brief  查找最大子串
 3  * @note   
 4  * @param  *Original: 最短的输入字符串
 5  * @param  ArrayStrSize: 字符串数组大小
 6  * @retval 字串长度
 7  * @author 杨文蓁的小迷弟
 8  */
 9 int SearchMax(char *Original, int ArrayStrSize)
10 {
11     bool flag; //崩坏标志
12     int len = strlen(Original);
13     char Normal[MAXSIZE];
14     char Reversed[MAXSIZE]; //用来存储逆序
15 
16     //比较次数
17     for (int i = len; i >= 1; i--)
18     {
19         //从第j处开始取字串
20         for (int j = 0; j <= len - i; j++)
21         {
22             strncpy(Normal, Original + j, i);
23             strncpy(Reversed, Original + j, i);
24             Normal[i] = '\0';
25             Reversed[i] = '\0';
26             strrev(Reversed);
27             //reverse(Reversed);//备用翻转函数
28 
29             flag = 1;
30 
31             for (int row = 0; row < ArrayStrSize; row++)
32             {
33                 if (-1 == KMP(ArrayStr[row], Normal) && -1 == KMP(ArrayStr[row], Reversed))
34                 {
35                     flag = 0;
36                     break;
37                 }
38             }
39             if (flag)
40             {
41                 return i;
42             }
43         }
44     }
45     return 0;
46 }

 

 

      正如我上面所说的,三重for,两重控制strncpy来改变模式串长度及减小的方向,一个控制比较的主串的变化。

if (-1 == KMP(ArrayStr[row], Normal) && -1 == KMP(ArrayStr[row], Reversed))

这里等于-1 是由于我写的kmp匹配失败返回的为-1。匹配成功的话,由于我的最外层for从len-1开始,所以直接就是最长,不成功的话继续匹配。

 

  最后我贴一下最终的代码

  1 /**
  2  * @brief  字串
  3  * @note   strrevn函数只在gcc中可用Linux和cb都是gcc的解释器
  4  * @author 杨文蓁的小迷弟
  5  */
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9 #include <stdbool.h>
 10 #define MAXSIZE 101
 11 
 12 char ArrayStr[MAXSIZE][MAXSIZE];
 13 
 14 void GetNext(char *Pattern, int next[]);
 15 int KMP(char *MainString, char *Pattern);
 16 int SearchMax(char *Original, int ArrayStrSize);
 17 void strrevn(char *str1, char *str2);
 18 void reverse(char *str);
 19 
 20 int main()
 21 {
 22     int times;
 23     int ArrayStrSize;
 24 
 25     scanf("%d", &times);
 26     int index = 0;
 27     for (int i = 0; i < times; i++)
 28     {
 29         scanf("%d", &ArrayStrSize);
 30         int min = MAXSIZE;
 31         for (int j = 0; j < ArrayStrSize; j++)
 32         {
 33             scanf("%s", ArrayStr[j]);
 34             if (min > strlen(ArrayStr[j]))
 35             {
 36                 min = strlen(ArrayStr[j]);
 37                 index = j;
 38             }
 39         }
 40 
 41         printf("%d\n", SearchMax(ArrayStr[index], ArrayStrSize));
 42     }
 43     return 0;
 44 }
 45 /**
 46  * @brief  查找最大子串
 47  * @note   
 48  * @param  *Original: 最短的输入字符串
 49  * @param  ArrayStrSize: 字符串数组大小
 50  * @retval 字串长度
 51  */
 52 int SearchMax(char *Original, int ArrayStrSize)
 53 {
 54     bool flag; //崩坏标志
 55     int len = strlen(Original);
 56     char Normal[MAXSIZE];
 57     char Reversed[MAXSIZE]; //用来存储逆序
 58 
 59     //比较次数
 60     for (int i = len; i >= 1; i--)
 61     {
 62         //从第j处开始取字串
 63         for (int j = 0; j <= len - i; j++)
 64         {
 65             strncpy(Normal, Original + j, i);
 66             strncpy(Reversed, Original + j, i);
 67             Normal[i] = '\0';
 68             Reversed[i] = '\0';
 69             strrev(Reversed);
 70             //reverse(Reversed);//备用翻转函数
 71 
 72             flag = 1;
 73 
 74             for (int row = 0; row < ArrayStrSize; row++)
 75             {
 76                 if (-1 == KMP(ArrayStr[row], Normal) && -1 == KMP(ArrayStr[row], Reversed))
 77                 {
 78                     flag = 0;
 79                     break;
 80                 }
 81             }
 82             if (flag)
 83             {
 84                 return i;
 85             }
 86         }
 87     }
 88     return 0;
 89 }
 90 
 91 /**
 92  * @brief  字符串反转函数
 93  * @note   备用
 94  * @param  *str: 要反转的字符串
 95  * @retval None
 96  */
 97 void reverse(char *str)
 98 {
 99     int len = strlen(str);
100 
101     for (int i = 0; i < len / 2; i++)
102     {
103         char temp = str[i];
104         str[i] = str[len - i - 1];
105         str[len - i - 1] = temp;
106     }
107 }
108 
109 /**
110  * @brief  字符串反转函数(二)
111  * @note   这种更像接口函数
112  * @param  *str1: 被反转的字符串
113  * @param  *str2: 反转后存储的地方
114  * @retval None
115  */
116 void strrevn(char *str1, char *str2)
117 {
118     int l;
119     int i;
120 
121     l = strlen(str1);
122     for (i = 0; i < l; i++)
123     {
124         str2[i] = str1[l - 1 - i];
125     }
126 }
127 
128 /**
129  * @brief  KMP(字串查找)
130  * @note   匹配失败返回-1
131  * @param  *MainString: 主串
132  * @param  *Pattern: 模式串
133  * @retval 最长匹配字串的第一次出现的第一个字母的位置
134  */
135 int KMP(char *MainString, char *Pattern)
136 {
137     int next[MAXSIZE] = {0};
138     GetNext(Pattern, next);
139 
140     int i = 0;
141     int j = 0;
142     int s_len = strlen(MainString);
143     int PatternLen = strlen(Pattern);
144 
145     while (i < s_len && j < PatternLen)
146     {
147         if (j == -1 || MainString[i] == Pattern[j])
148         {
149             i++;
150             j++;
151         }
152         else
153         {
154             j = next[j];
155         }
156     }
157 
158     if (j == PatternLen)
159     {
160         return i - j;
161     }
162 
163     return -1;
164 }
165 
166 /**
167  * @brief  生成转跳数组(未优化)
168  * @note   不要使用优化版
169  * @param  *Pattern: 模式串
170  * @param  next[]: 转跳数组
171  * @retval None
172  */
173 void GetNext(char *Pattern, int next[])
174 {
175     int PatternLen = strlen(Pattern);
176     int i = 0;
177     int j = -1;
178     next[0] = -1;
179 
180     while (i < PatternLen - 1)
181     {
182         if (j == -1 || Pattern[i] == Pattern[j])
183         {
184             i++;
185             j++;
186             next[i] = j;
187         }
188         else
189         {
190             j = next[j];
191         }
192     }
193 }
KMP款

 

  再贴一个Strstr款的

 1 /**
 2  * @brief  子串(纯调用string库版本)
 3  * @note   strrevn函数只在gcc中可用Linux和cb都是gcc的解释器
 4  * @author 杨文蓁的小迷弟
 5  */
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 #include <stdbool.h>
10 #define MAXSIZE 101
11 
12 char ArrayStr[MAXSIZE][MAXSIZE];
13 
14 void GetNext(char *Pattern, int next[]);
15 int KMP(char *MainString, char *Pattern);
16 int SearchMax(char *Original, int ArrayStrSize);
17 void strrevn(char *str1, char *str2);
18 void reverse(char *str);
19 
20 int main()
21 {
22     int times;
23     int ArrayStrSize;
24 
25     scanf("%d", &times);
26     int index = 0;
27     for (int i = 0; i < times; i++)
28     {
29         scanf("%d", &ArrayStrSize);
30         int min = MAXSIZE;
31         for (int j = 0; j < ArrayStrSize; j++)
32         {
33             scanf("%s", ArrayStr[j]);
34             if (min > strlen(ArrayStr[j]))
35             {
36                 min = strlen(ArrayStr[j]);
37                 index = j;
38             }
39         }
40 
41         printf("%d\n", SearchMax(ArrayStr[index], ArrayStrSize));
42     }
43     return 0;
44 }
45 /**
46  * @brief  查找最大子串
47  * @note   
48  * @param  *Original: 最短的输入字符串
49  * @param  ArrayStrSize: 字符串数组大小
50  * @retval 字串长度
51  */
52 int SearchMax(char *Original, int ArrayStrSize)
53 {
54     bool flag; //崩坏标志
55     int len = strlen(Original);
56     char Normal[MAXSIZE];
57     char Reversed[MAXSIZE]; //用来存储逆序
58 
59     //比较次数
60     for (int i = len; i >= 1; i--)
61     {
62         //从第j处开始取字串
63         for (int j = 0; j <= len - i; j++)
64         {
65             strncpy(Normal, Original + j, i);
66             strncpy(Reversed, Original + j, i);
67             Normal[i] = '\0';
68             Reversed[i] = '\0';
69             strrev(Reversed);
70             //reverse(Reversed);//备用翻转函数
71 
72             flag = 1;
73 
74             for (int row = 0; row < ArrayStrSize; row++)
75             {
76                 if (NULL == strstr(ArrayStr[row], Normal) && NULL == strstr(ArrayStr[row], Reversed))
77                 {
78                     flag = 0;
79                     break;
80                 }
81             }
82             if (flag)
83             {
84                 return i;
85             }
86         }
87     }
88     return 0;
89 }
纯调用版

  如果strrev不可用,那自行写一个反转函数。

 

更新日期 2019-10-11

  拿c++实现了一下,感兴趣的可以看看,用的是c++11的标准,除了功能函数之外,基本用自带函数实现。

 1 #include <iostream>
 2 #include <vector>
 3 #include <String>
 4 #include <algorithm>
 5 
 6 class SubString
 7 {
 8 public:
 9     int SearchMax(std::vector<std::string> StrArray, int Index);
10 };
11 
12 int main()
13 {
14     using std::cin;
15     using std::cout;
16     using std::string;
17     using std::vector;
18 
19     int times, index = 0;
20     int StrArraySize;
21     cin >> times;
22     for (int i = 0; i < times; ++i)
23     {
24         vector<string> StrArray;
25         cin >> StrArraySize;
26         int min = 101;
27         for (int j = 0; j < StrArraySize; ++j)
28         {
29             string InputStrtemp;
30             cin >> InputStrtemp;
31             StrArray.push_back(InputStrtemp);
32             if (min > StrArray[j].size())
33             {
34                 min = StrArray[j].size();
35                 index = j;
36             }
37         }
38         SubString SUBSTR;
39         int result = SUBSTR.SearchMax(StrArray, index);
40         cout << result << '\n';
41         StrArray.clear();
42     }
43     return 0;
44 }
45 
46 int SubString::SearchMax(std::vector<std::string> StrArray, int Index)
47 {
48     bool Flag = true;
49     int StrArrayLen = StrArray.size();
50     int MinStrLen = StrArray[Index].size();
51     std::string NormalStr, ReverseStr;
52 
53     for (int i = MinStrLen; i >= 1; --i)
54     {
55         for (int j = 0; j <= MinStrLen - i; ++j)
56         {
57             Flag = true;
58             NormalStr = StrArray[Index].substr(j, i);
59             ReverseStr = NormalStr;
60             reverse(ReverseStr.begin(), ReverseStr.end());
61 
62             for (int k = 0; k < StrArrayLen; ++k)
63             {
64                 if (std::string::npos == StrArray[k].find(NormalStr) && std::string::npos == StrArray[k].find(ReverseStr))
65                 {
66                     Flag = false;
67                     break;
68                 }
69             }
70             if (Flag)
71             {
72                 return i;
73             }
74         }
75 
76     }
77     return 0;
78
 1 #include <iostream>
 2 #include <vector>
 3 #include <string>
 4 #include <algorithm>
 5 
 6 class SubString
 7 {
 8 public:
 9     int SearchMax(std::vector<std::string> StrArray, int Index);
10 };
11 
12 int main()
13 {
14     using std::cin;
15     using std::cout;
16     using std::string;
17     using std::vector;
18 
19     int times, index = 0;
20     int StrArraySize;
21     cin >> times;
22     for (int i = 0; i < times; ++i)
23     {
24         vector<string> StrArray;
25         cin >> StrArraySize;
26         int min = 101;
27         for (int j = 0; j < StrArraySize; ++j)
28         {
29             string InputStrtemp;
30             cin >> InputStrtemp;
31             StrArray.push_back(InputStrtemp);
32             if (min > StrArray[j].size())
33             {
34                 min = StrArray[j].size();
35                 index = j;
36             }
37         }
38         SubString SUBSTR;
39         int result = SUBSTR.SearchMax(StrArray, index);
40         cout << result << '\n';
41         StrArray.clear();
42     }
43     return 0;
44 }
45 
46 int SubString::SearchMax(std::vector<std::string> StrArray, int Index)
47 {
48     bool Flag = true;
49     int StrArrayLen = StrArray.size();
50     int MinStrLen = StrArray[Index].size();
51     std::string NormalStr, ReverseStr;
52 
53     for (int i = MinStrLen; i >= 1; --i)
54     {
55         for (int j = 0; j <= MinStrLen - i; ++j)
56         {
57             Flag = true;
58             NormalStr = StrArray[Index].substr(j, i);
59             ReverseStr = NormalStr;
60             reverse(ReverseStr.begin(), ReverseStr.end());
61 
62             for (int k = 0; k < StrArrayLen; ++k)
63             {
64                 if (std::string::npos == StrArray[k].find(NormalStr) && std::string::npos == StrArray[k].find(ReverseStr))
65                 {
66                     Flag = false;
67                     break;
68                 }
69             }
70             if (Flag)
71             {
72                 return i;
73             }
74         }
75 
76     }
77     return 0;
78 }
C++11

 

算法不易,诸君共勉!

posted @ 2019-10-10 13:36  秦_殇  阅读(482)  评论(0编辑  收藏  举报