#include <iostream>
#include <stdio.h>
#include <sstream>
#include <stdlib.h>//qsort
using namespace std;
/*
问题:给定一个文本文件作为输入,查找其中最长的重复子字符串。例如,“Ask not what your country can do for you, but what you can do for your country”中
最长的重复字符串是"can do for you",第二长的是“your country”。
分析:这个是寻找最长重复字符串,前缀树trie适用于进行前缀和字符串搜索,后缀树适合于查找子串在字符串中出现位置,这两种输入结构需要制定输入的查找字符串,
但这道题目不会给出输入的查找字符串,因此这两种方法不行。
最长公共子序列求的是两个字符串公共部分,也不适用该方法。
最短摘要生成方法,是需要给定搜索的字符串数组,不适用该题。
暴力破解方法:罗列子串的起始位置,设句子长度为n,罗列的时间复杂度为O(N^2),然后对每个子字符串采用后缀树来做,寻找出后缀树中至少包含两个起始位置的子串
作为结果返回,然后从中选择出长度最长的。
后缀树建树时间:O(N^2),查找时间O(N),总的时间复杂度:O(N^3)
书上解法:
1 简单解法:遍历两个字符串的起始位置,分别p,q,对p,q查找公共字符串的长度,查找时间为O(N),遍历两个子串的起始位置是O(N),总的时间复杂度为O(N^3)
2 后缀数组:遍历字符串str的起始位置i(i从0到字符串长度length-1),产生后缀数组str[0...length-1],str[1...length-1],..str[length-1...length-1],
对字符串数组进行排序,使得后缀相近的子串集中在一起,对排序后的子串扫描比较相邻远古三,找出最长重复的字符串。
时间复杂度为O(NLogN * N),最后面的n是字符串比较的时间复杂度
输入:
abcdabcd
输出:
abcd
关键:
1
1】 简单解法:遍历两个字符串的起始位置,分别p,q,对p,q查找公共字符串的长度,查找时间为O(N),遍历两个子串的起始位置是O(N),总的时间复杂度为O(N^3)
2】后缀数组:遍历字符串str的起始位置i(i从0到字符串长度length-1),产生后缀数组str[0...length-1],str[1...length-1],..str[length-1...length-1],
对字符串数组进行排序,使得后缀相近的子串集中在一起,对排序后的子串扫描比较相邻远古三,找出最长重复的字符串。
时间复杂度为O(NLogN * N),最后面的n是字符串比较的时间复杂度
2
//字符串比较,这里发现传入的是字符串的首字符地址后,发现没有排序成功
int myStrcmp(const void* p1 , const void* p2)
{
char* ptr1 = * ( (char**)p1 );//由于是传入的字符地址: char* 是字符的地址 , char* *:是指向字符地址的地址,即字符串的地址,前面为什么又加 *
char* ptr2 = * ( (char**)p2 );
return strcmp(ptr1 , ptr2);
}
3 qsort(suffixArr , length , sizeof(char*) , myStrcmp);//第三个是待排序数组中每个元素大小,由于每个元素是char* ,所以是sizeof(char*)
*/
const int MAXSIZE = 1024;
int commonLength(char* str1 , char* str2)
{
if(NULL == str1 || NULL == str2)
{
return 0;
}
int length = 0;
while( *(str1++) == *(str2++) )
{
length++;
};
return length;
}
string findMaxRepeatedSubString(char* str)
{
if(NULL == str)
{
return "";
}
int size = strlen(str);
int max = INT_MIN;
int maxIndex = 0;
for(int i = 0 ; i < size ; i++)
{
for(int j = i + 1 ; j < size ; j++)
{
int length = commonLength(&str[i] , &str[j]);
if(length > max)
{
max = length;
//需要记录后面开始的字符串的起始位置
maxIndex = j;
}
}
}
stringstream stream;
//获取最大重复子串
for(int i = maxIndex ; i < maxIndex + max ; i++)
{
stream << str[i];
}
return stream.str();
}
//字符串比较,这里发现传入的是字符串的首字符地址后,发现没有排序成功
int myStrcmp(const void* p1 , const void* p2)
{
/*
char* ptr1 = (char*)p1;
char* ptr2 = (char*)p2;
*/
char* ptr1 = * ( (char**)p1 );//由于是传入的字符地址: char* 是字符的地址 , char* *:是指向字符地址的地址,即字符串的地址,前面为什么又加 *
char* ptr2 = * ( (char**)p2 );
return strcmp(ptr1 , ptr2);
}
//采用后缀数组,查找最长重复子串,
string findMaxRepeatedSubString_suffixArray(char* str)
{
if(NULL == str)
{
return 0;
}
int length = strlen(str);
if(length > MAXSIZE)
{
cout << "string length is " << length << ",it must less than " << MAXSIZE;
return 0;
}
char* suffixArr[MAXSIZE];//后缀数组,每个元素是一个指向后缀数组的指针
for(int i = 0 ; i < length; i++)
{
suffixArr[i] = &str[i];
}
//对后缀数组排序
qsort(suffixArr , length , sizeof(char*) , myStrcmp);//第三个是待排序数组中每个元素大小,由于每个元素是char* ,所以是sizeof(char*)
//接下来比较后缀数组相邻元素,查找最大长度
int max = INT_MIN;
char* maxPtr;
for(int i = 1 ; i < length; i++)
{
int length = commonLength(suffixArr[i-1] , suffixArr[i]);
if(length > max)
{
max = length;
maxPtr = suffixArr[i-1];//由于后缀数组从小到大排的,前面的才是最大重复子串
}
}
stringstream stream;
//获取最大重复子串
for(char* s = maxPtr ; s < maxPtr + max ; s++)
{
stream << (*s);
}
return stream.str();
}
void process()
{
char str[MAXSIZE];
while(scanf("%s" , str))
{
//string result = findMaxRepeatedSubString(str);
string result = findMaxRepeatedSubString_suffixArray(str);
cout << result << endl;
}
}
int main(int argc , char* argv[])
{
process();
getchar();
return 0;
}
编程珠玑: 15章 字符串 15.2寻找字符串中的最长重复子串 -------解题总结
最新推荐文章于 2024-07-31 19:19:10 发布
本文探讨了如何找到一个文本中最长的重复子字符串。通过比较两种方法,简单解法的时间复杂度为O(N^3),而使用后缀数组的方法可以达到O(NLogN * N)。后缀数组方法通过对字符串的后缀排序,然后比较相邻子串来找到最长重复部分。
摘要由CSDN通过智能技术生成