自己写的一个后缀树算法查找一个字符串的最长重复子串

在上个星期面试一家公司的笔试题上面的最后一道题就是写程序查找一个字符串的最长重复子串。当时想了很长时间没想出什么好方法,就把一个算法复杂度比较高的算法写上去了。回来上机把那个算法写了一遍测试没问题,然后自己又到网上面查查还有什么方法,然后发现好像有种叫做后缀树的方法,然后看那个方法,当时没给出代码,看图看了老半天加之自己想了好几个小时终于知道后缀树是个什么东西。然后自己萌生了一个自己写一个后缀树算法解决那个重复子串的问题。然后写了一天终于写出来了。后续有做了一些测试,发现自己写的一个只有几十个字母的字符串比之前的那个算法慢了好几十倍,想了很久想不出原因,后来自己随机生成了一个10000个字符的字符串使用后缀树算法比旧的方法快了4倍,所以后缀树算法还是一个比较优秀的算法的。但是为了以后能够回来看下自己写的东西,所以就写这篇博客记录一下自己写的后缀树算法的源代码。一下是代码

class SuffixTreeNode;
typedef SuffixTreeNode* SuffixTreeNodePtr;

class SuffixTreeNode {
public:
	SuffixTreeNode();
	void initNode();
	SuffixTreeNodePtr& returnChildsAt(int i);
	int cmpSameLength(const char *start);
	void setHead(const char *start);
	const char* getHead();
	void setLen(int length);
	int getLen();
	void setStartPos(int pos);
	int getStartPos();
private:
	const char *pHead;
	int len;
	int start;
	SuffixTreeNode* childs[256];
};

class SuffixTree {
public:
	SuffixTree();
	~SuffixTree();
	int insert(const char *start, int pos);
private:
	SuffixTreeNode* allocNode();
	bool allocBufferNode(int size = 1024);
	int innerInsert(SuffixTreeNode *pNode, const char *start, int pos, int preSameLength);

	SuffixTreeNode* root;
	SuffixTreeNode* freeNode;
	int maxStrLen;
	std::vector<SuffixTreeNode*> nodeBuff;
};

SuffixTreeNode::SuffixTreeNode() {
	initNode();
}

void SuffixTreeNode::initNode() {
	memset(this, 0, sizeof(SuffixTreeNode));
}

SuffixTreeNodePtr& SuffixTreeNode::returnChildsAt(int i) {
	return childs[i];
}

int SuffixTreeNode::cmpSameLength(const char *start) {
	int length = 0;
	if (pHead != NULL)
		for (; (length < len) && (pHead[length] == start[length]); length++);
	else
		return 0;
	return length;
}

void SuffixTreeNode::setHead(const char *start) {
	pHead = start;
}

const char* SuffixTreeNode::getHead() {
	return pHead;
}

void SuffixTreeNode::setLen(int length) {
	len = length;
}

int SuffixTreeNode::getLen() {
	return len;
}

void SuffixTreeNode::setStartPos(int pos) {
	start = pos;
}

int SuffixTreeNode::getStartPos() {
	return start;
}

SuffixTree::SuffixTree() : root(NULL), freeNode(NULL){
}

SuffixTree::~SuffixTree() {
	for (size_t i = 0; i < nodeBuff.size(); i++) {
		SuffixTreeNode* pNode = nodeBuff.at(i);
		if (pNode != NULL) {
			free(pNode);
		}
	}
}

bool SuffixTree::allocBufferNode(int size) {
	SuffixTreeNode *pNode = (SuffixTreeNode*)malloc(sizeof(SuffixTreeNode) * size);

	if (pNode == NULL) {
		return false;
	}

	nodeBuff.push_back(pNode);

	for (int i = 0; i < size; i++) {
		pNode->returnChildsAt(0) = freeNode;
		freeNode = pNode;
		pNode++;
	}

	return true;
}

SuffixTreeNode* SuffixTree::allocNode() {
	if (freeNode == NULL) {
		if (!allocBufferNode())
			return NULL;
	}

	assert(freeNode != NULL);

	SuffixTreeNode* pNode = freeNode;
	freeNode = freeNode->returnChildsAt(0);

	return pNode;
}

int SuffixTree::insert(const char *start, int pos) {
	if (root == NULL) {
		root = allocNode();
		if (root == NULL) {
			return 0;
		}

		root->initNode();

		maxStrLen = strlen(start);

	}

	return innerInsert(root, start, pos, 0);
}

int SuffixTree::innerInsert(SuffixTreeNode *pNode, const char *start, int pos, int preSameLength) {
	if (pNode == NULL)
		return 0;
	int sameLength = pNode->cmpSameLength(start);
	if (sameLength < pNode->getLen()) {
		SuffixTreeNode *pRetNode = allocNode();
		if (pRetNode == NULL) {
			return 0;
		}

		pRetNode->initNode();
		pRetNode->setHead(pNode->getHead() + sameLength);
		pRetNode->setLen(pNode->getLen() - sameLength);
		pRetNode->setStartPos(pNode->getStartPos());
		pNode->setLen(sameLength);
		for (int i = 0; pNode->returnChildsAt(i) != NULL; i++) {
			pRetNode->returnChildsAt(i) = pNode->returnChildsAt(i);
			pNode->returnChildsAt(i) = NULL;
		}
		pNode->returnChildsAt(0) = pRetNode;

		pRetNode = allocNode();
		if (pRetNode == NULL) {
			return 0;
		}

		pRetNode->initNode();
		pRetNode->setHead(start + sameLength);
		pRetNode->setLen(maxStrLen - (pos + preSameLength + sameLength));
		pRetNode->setStartPos(pos);
		pNode->returnChildsAt(1) = pRetNode;
	}
	else if (sameLength == pNode->getLen()) {
		int index = 0;
		for (;pNode->returnChildsAt(index) != NULL; index++) {
			if ((pNode->returnChildsAt(index)->getHead())[0] == start[sameLength]) {
				return sameLength + innerInsert(pNode->returnChildsAt(index), start + sameLength, pos, preSameLength + sameLength);
			}
		}
		SuffixTreeNode *pRetNode = allocNode();
		if (pRetNode == NULL) {
			return 0;
		}

		pRetNode->initNode();
		pRetNode->setHead(start + sameLength);
		pRetNode->setLen(maxStrLen - (pos + preSameLength + sameLength));
		pRetNode->setStartPos(pos);
		pNode->returnChildsAt(index) = pRetNode;
	}

	return sameLength;
}

string findMax(string &ret) {
	int maxLen = 0;
	int maxPos = 0;
	SuffixTree tree;
	const char *str = ret.c_str();
	for (int i = 0; str[i] != ‘\0‘; i++) {
		int curLen = tree.insert(str + i, i);
		if (curLen > maxLen) {
			maxLen = curLen;
			maxPos = i;
		}
	}

	return ret.substr(maxPos, maxLen);
}

findMax函数就是那个找到最长重复子串那个函数了。

时间: 2024-12-09 21:41:42

自己写的一个后缀树算法查找一个字符串的最长重复子串的相关文章

后缀数组求解字符串的最长重复子串

后缀数组 给定一个字符串,求出其最长的重复子串. 思路:使用后缀数组,对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分.这样的时间复杂度为: 生成后缀数组 O(N)排序 O(NlogN*N) 最后面的 N 是因为字符串比较也是 O(N)依次检测相邻的两个字符串 O(N * N)总的时间复杂度是 O(N^2*logN),

poj2774 后缀数组2个字符串的最长公共子串

Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 26601   Accepted: 10816 Case Time Limit: 1000MS Description The little cat is majoring in physics in the capital of Byterland. A piece of sad news comes to him these days

poj 1743 二分答案+后缀数组 求不重叠的最长重复子串

题意:给出一串序列,求最长的theme长度 (theme:完全重叠的子序列,如1 2 3和1 2 3  or  子序列中每个元素对应的差相等,如1 2 3和7 8 9) 要是没有差相等这个条件那就好办多了,直接裸题. 一开始想了个2B方法,后来发现真心2B啊蛤蛤蛤 1 for i=1 to 88 do 2 { 3 for j=1 to length 4 { 5 r2[j]=r[j]+i; 6 if (r2[j]>88) r2[i]-=88; 7 } 8 把新序列r2连接到原序列r的后面 9 pr

后缀数组(多个字符串的最长公共子串)—— POJ 3294

对应POJ 题目:点击打开链接 Life Forms Time Limit:6666MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Problem C: Life Forms You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial tra

找出一个字符串中最长重复次数的子字符串,并计算其重复次数

原题 找出一个字符串中最长重复次数的子字符串,并计算其重复次数.例如:字符串"abc fghi bc kl abcd lkm abcdefg",并返回"abcd"和2. 我的思路 为了方便表述,我们使用变量src作为原字符串,sub_str作为子字符串. 由于题目要求寻找至少重复2次的最长的子字符串,重点在于最长的子字符串,而不在于重复的最多次数.因此我们可以从长度最长的字符串入手,计算其重复次数.只要重复达到2次,即可返回该字符串. 显然长度最长的子字符串就是原串

面试题[后缀数组]: 最长重复子串

题目:给定一个字符串,求出最长重复子串. 这个题目可以用后缀数组来解:对后缀数组排好序,这样重复的子串就在相邻的后缀中找就可以了.我的C++代码实现如下: class Solution { public: string LongestRepeatingSubstring(string str) { size_t len = str.size(); vector<string> SuffixArray(len); for (size_t i = 0; i < len; ++i) Suffi

POJ 3261 可重叠的 k 次最长重复子串【后缀数组】

这也是一道例题 给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠.算法分析:这题的做法和上一题差不多,也是先二分答案,然后将后缀分成若干组.不同的是,这里要判断的是有没有一个组的后缀个数不小于 k.如果有,那么存在k 个相同的子串满足条件,否则不存在.这个做法的时间复杂度为 O(nlogn). Source Code: //#pragma comment(linker, "/STACK:16777216") //for c++ Compiler #include

POJ - 3261 Milk Patterns (后缀数组求可重叠的 k 次最长重复子串)

Description Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation, he discovered that although he can't predict the quality of milk from one day to the next, there are some regular pattern

Python 封装一个函数,查找文字字符串数字英文下标

def abc(str,data): count = [] numMax = 0 for a in range(len(str)): if a == 0: temp = str.find(data, numMax, len(str)) else: temp = str.find(data, numMax+1, len(str)) if temp != -1: for i in range(len(data)): count.append(temp + i) elif numMax == 0 an