哎,这次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", ×);
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 }
再贴一个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", ×);
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 }
算法不易,诸君共勉!