剑指offer学习笔记 最长不含重复字符的子字符串

面试题48:最长不含重复字符的子字符串。请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。假设字符只包含’a’~'z’的字符。如在字符串"arabcacfr"中,最长的不含重复字符的子字符串是"acfr",长度为4。

我们不难找出字符串的所有子字符串,然后判断每个子字符串中是否包含重复的字符,这种蛮力法的唯一缺点就是效率,一个长为n的字符串中有O(n²)个子字符串,并且需要O(n)的时间判断子字符串中是否包含重复字符,该解法的总时间复杂度是O(n²)。

动态规划解法:首先定义函数f(i)表示以第i个字符为结尾的不包含重复字符的子字符串的最长长度。我们从左到右逐一扫描字符串中的每个字符,因此当我们计算以第i个字符为结尾的不包含重复字符的子字符串的最长长度f(i)时,此时我们已经知道f(i-1)了。

如果第i个字符之前没有出现过,那么f(i)=f(i-1)+1,如在串"arabcacfr"中,显然f(0)=1,在计算f(1)时,下标为1的字符’r’之前没有出现过,因此f(1)=f(0)+1=2。

如果第i个字符之前已经出现过,我们需要先计算第i个字符和它上次出现在字符串中的位置的距离,记为d,接着分两种情形:一是d小于等于f(i-1),此时第i个字符上次出现在f(i-1)对应的最长字串之中,因此,f(i)=d,同时这也意味着在第i个字符出现两次所夹的子串中再也没有其他重复字符了。以上例分析,我们计算f(2)时,'a’已经在前面出现过,且d为2,d<=f(2-1)=f(1)=2,因此,f(2)=2,对应的最长不含重复字符的子串是"ra";二是d>f(i-1),说明此时第i个字符上次出现在f(i-1)对应的最长不含重复字符的子串之前,因此仍然有f(i)=f(i-1)+1:

#include <iostream>
#include <string>
using namespace std;

int LongestSubstringWithoutDuplication(const string& s) {
	int currLength = 0, maxLength = 0;

	int* pos = new int[26]();    //创建动态数组并值初始化元素为0,存储的是字符上次出现的位置
	for (int i = 0; i < 26; ++i) {
		pos[i] = -1;    //-1说明上次没出现过
	}

	for (int i = 0; i < s.size(); ++i) {
		int p = pos[s[i] - 'a'];
		if (p == -1 || i - p > currLength) {    //如下标为i的字符没出现过或出现过但d>current
			++currLength;

			if (currLength > maxLength) {    //判断正常增加是否破纪录
				maxLength = currLength;
			}
		}
		else {    //否则应该更新curr为d,这样会使curr变小
			if (currLength > maxLength) {    //变小前先判断是否破了记录
				maxLength = currLength;
			}

			currLength = i - p;
		}
		pos[s[i] - 'a'] = i;
	}
	delete[] pos;
	return maxLength;
}

int main() {
	cout << LongestSubstringWithoutDuplication("arabcacfr") << endl;
}
发布了211 篇原创文章 · 获赞 11 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/tus00000/article/details/105053855