题目
编写算法,对给定字符串str,返回其最长重复子串及其下标位置。
eg:str=“abcdacdac”,则子串“cdac”是str的最长重复子串,下标为2
题目分析
1. 第一步:遍历字符串得到所有子串的特征向量N
2. 第二步:找到所有子串的最大特征向量NN_max
3. 第三步:返回最长重复字符串的下标
实现细节
-
遍历字符串中的每个字符,根据前缀子串和左子串匹配的方式,得到每个字符对应的特征值N;
-
将a中得到的所有字符串对应的特征值比较,将最大值保存在数组N_Store中;
-
去掉字符串第一个字符,再次重复a,b。并立即比较N_Store数组中存放的几个最大特征向量的大小,将大的一个保存在变量NN_max中,并用index_NN_max记录此时对应元素在N_Store数组中的下标;
- 如果删除后剩余的字符串长度比NN_max小则不再进行删除操作,否则继续删除,即重复c操作;
- 最后得到index_NN_max是最长重复子串的起始位置,NN_max是最长重复子串的长度;
函数及其功能
getmax_N(string P,int* N):求特征向量数组中最大的特征值
Next(string P):
- 求出字符串P的每个字符对应的特征向量;
- 求出1中所有特征向量的最大值,将该值存入数组N_Store中;
- 如果已经进行了多次该操作,那么比较这几次操作每次所得最大特征向量的最大值,得到目前最长重复子串的长度和起始下标,之后每递归一次,就更新一次最长子串长度和对应起始下标,直到找到目标值就不再更新;
main():返回得到的最长重复子串和其对应起始位置;
使用参数及具体函数
#define SIZE 1000
int N_Store[SIZE] = {0}; // 用来存放每次删除操作后,得到的字符串特征向量的最大值
int NN_max = N_Store[0]; // 用来存放所有最大值中的最大值,即最长重复子串长度
static int countn = 0; // 用来存放N_Store数组的有效值长度
int index_NN_max = 0; // 存放最长重复子串的起始下标
getmax_N(string P,int* N) :
/*
求特征向量数组中最大的特征值
P:特征向量数组对应的字符串
N:特征向量数组
maxN:本次特征向量数组中的最大值
*/
int getmax_N(string P,int* N) {
int maxN = N[0];
int n = 0;
while (n<P.length()) // 遍历数组N(因为N存放的是本次字符串P的特征值向量数组,所以直接用P.length),取最大值
{
maxN = max(N[n++], maxN);
}
return maxN;
}
Next(string P)
/*
特征向量更新函数
P:要判断的字符串;
m:字符串长度;
N[]:字符串对应的特征向量数组;
完成工作:
1. 求出字符串P的每个字符对应的特征向量
2. 求出1中所有特征向量的最大值,将该值存入数组N_Store中
3. 如果已经进行了多次该操作,那么比较这几次操作每次所得最大特征向量的最大值
*/
int* Next(string P) {
int m = P.length();
assert(m > 0);
int* N = new int[m];
assert(N != 0);
N[0] = 0; // 设定特征向量第一个值为0
for (int i = 1; i < m; ++i) { // 遍历字符串
int k = N[i - 1]; // 找到i位置上一个的特征值N[i-1]
while (k > 0 && P[k] != P[i]) // 画图表示
{
k = N[k - 1];
}
N[i] = (P[i] == P[k]) ? k + 1 : 0; // 图上表示
}
int maxN = getmax_N(P,N); // 调用函数,求本次的P字符串的特征向量最大值N
cout << "特征向量数组中最大的特征值是:" << endl;
cout << maxN << endl;
N_Store[countn] = maxN; // 将本次P字符串的特征向量最大值放入N_Store数组中
int flag_maxN = NN_max;
if (countn >= 1) {
NN_max = max(NN_max, N_Store[countn-1]); // 求出当前N_Store数组中最大的特征向量(最大的最大)
if (NN_max != flag_maxN) { // 当最大特征值更新之后,对应的下标也更新
index_NN_max = countn - 1; // 记录最长重复子串的起始下标
}
}
countn++; // countn+1,让N_Store数组后移
if (P.length() > NN_max&& P.length() > 1) { // 删除字符串中第一个元素的判断标准,如果当前字符串的长度已经比当前所得最大特征向量值小的话,就无需再删除
for (int t = 0; t < m - 1; t++) {
P[t] = P[t + 1]; // 这时候最后一个元素是没有删除的,担心越界,所以设置的m-1
}
if (!P.empty()) {
P.pop_back(); // 将字符串最后一个位置的元素删除
}
Next(P); // 递归操作,将删除了首个元素之后的字符串,再次进行特征值的计算,求所有特征值最大值等工作
}
return N;
}
main()
int main() {
static string str;
str = "abcdacdac";
string str_Store = str; // 该str_Store字符串用于储存最开始未进行删除操作的str字符串
Next(str); // 进行求字符串及其子串特征向量的工作,得到的N_Store数组包括了特征向量数组中最大的特征向量,以及最长重复子串的起始下标index_NN_max和长度NN_max
printf("删除%d个字符之后,得到的最大重复子串长度为:%d\n", index_NN_max, NN_max);
for (int out_max = index_NN_max; out_max < index_NN_max + NN_max; out_max++) {
cout << str_Store[out_max] << "";
}
}
程序运行结果
补充(特征向量N的代码解释)