编程珠玑: 15章 字符串 15.2寻找字符串中的最长重复子串 -------解题总结

来源:互联网 发布:ubuntu启动程序命令 编辑:程序博客网 时间:2024/05/22 00:27
#include <iostream>#include <stdio.h>#include <sstream>#include <stdlib.h>//qsortusing 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;}

0 0