子串子序列相关面试题总结

注:子串是连续的,子序列则不连续

1,求最长回文子串

中心法应该是最为普通的办法,时间复杂度O(n^2)。当然用后缀树或者后缀数组也可以降低时间复杂度,不过这个我不熟就不写了。输入字符串假设为”abcdedc";从左到右依次考虑以a为中心最长回文是多少,以ab中间的空为中心最长回文是多少,以b为中心最长回文是多少.......

代码如下:

int LPS(char *str, int n)
{
	assert(str != NULL);

	int len = 0;
	int i, temp;

	int l,r;

	for (i=0; i<n; i++)
	{
		l = i-1;
		r = i+1;
		temp = 1;
		while (l>=0 && r<n && str[l]==str[r])
		{
			l--;
			r++;
			temp = temp + 2;
		}
		if (temp > len)
			len = temp;

		l=i;
		r=i+1;
		temp = 0;
		while (l>=0 && r<n && str[l]==str[r])
		{
			l--;
			r++;
			temp = temp + 2;
		}
		if (temp > len)
			len = temp;
	}

	return len;
}

2,求最长回文子序列

 这道题可以用递归来解决。对于一个输入的数组,首尾下标分别为begin,end,函数原型为int LPS(int a[], int begin, int end);我们可以分为下列几种情况来讨论:

      1,begin>end;直接返回0;
      2,begin == end;只有一个元素,天然就是回文,返回1;
      3,begin<end时分两种情况讨论
           1) a[begin] == a[mid];应该返回 LPS(a, begin+1, end-1) + 2;两边向中间收拢。
           2) a[begin] != a[mid];这时有两种情况,应该返回较大值,即max(LPS(a,begin+1,end), LPS(a,begin,end-1))

根据上述思路形成递归代码:

int LPS(char *s, int begin, int end)
{
	assert(s != NULL);

	if (begin > end)
		return 0;
	if (begin == end)
		return 1;
	else
	{
		if (s[begin] == s[end])
			return LPS(s, begin+1, end-1) + 2;
		else
		{
			return max(LPS(s,begin+1,end), LPS(s,begin,end-1));
		}
	}
}

3,求最长公共子串

int LCS2( char *str1, char *str2)
{
	int len1 = strlen(str1);
	int len2 = strlen(str2);
	int *cl = new int[len2+1];
	
	int max = 0;
	int i, j;
	int start, end;

	for (i=0; i<=len2; i++)
	{
		cl[i]=0;
	}
	for (i=0; i<len1; i++)
	{
		for (j=len2-1; j>=0; j--)
		{
			if (str1[i] == str2[j])
			{

				cl[j+1] = cl[j] + 1;
				if (cl[j+1] > max)
				{
					max = cl[j+1];
					end = j;
				}
			}
			else
			{
				cl[j+1] = 0;
			}
		}	
	}

	start = end - max + 1;
	for (i=start; i<=end; i++)
	{
		cout << str2[i] << " " ;
	}

	return max;
}

4,求最长公共子序列

int LCS(char str1[], int astart, int aend, char str2[], int bstart, int bend)
{
	if (astart > aend || bstart > bend)
		return 0;

	if (str1[astart] == str2[bstart])
	{
		return LCS(str1,astart+1,aend,str2,bstart+1,bend) + 1;
	}
	else
	{
		return max(LCS(str1,astart+1,aend,str2,bstart,bend), LCS(str1,astart,aend,str2,bstart+1,bend));
	}
}

5,求最长连续递增子串

比如输入”abcd12345ed125ss123456789“,最长的为”123456789“,所以返回9。这个题比较简单,顺序遍历即可,时间复杂度O(n)。

代码:

int LIS(char *str)
{
	assert (str != NULL);
	
	int len = strlen(str);
	int cur = 1, max = 0;
	int start, end, temp = 0;

	for (int i = 1; i < len; i++)
	{
		if (str[i] == str[i-1] + 1)
		{
			cur++;
		}
		else				   //不满足连续递增了,立即更新一些状态
		{
			if (cur > max)
			{
				max = cur;
				start = temp;
				end = i-1;
			}
			temp = i;
			cur = 1;
		}

		if (i == len-1)       //这里是特殊处理,针对处于整个字符串末尾的最长字符串
		{
			if (cur > max)
			{
				max = cur;
				start = temp;
				end = i;
			}
		}
	}

	for (i = start; i <= end; i++)
		cout << str[i];
	cout << endl;

	return max;
}

6,求最长递增子序列

输入一个字符串str { "a d e f e g“},其最长递增子序列为 "a d f g“ 或者 ”a e f g “ 。该如何实现这个算法呢?我们定义一个数组lis[N]表示以下标 结尾的元素的最长递增子序列长度。lis[i]初始时为1,此时只有本身一个元素,但是下标i之前的元素可能大于str[i];所以要遍历从0到i-1的元素,检查是否有元素str[j]值大于str[i],若有且满足lis[j]+1>lis[i],lis[i]=lis[j]+1;最后扫描lis[N]求出最大值即可。这种方法整个时间复杂度O(n^2)。

代码:

int LIS(char *str)
{
	assert(str != NULL);

	int len = strlen(str);
	int i, j, max = 0;

	int *lis = new int[len];

	for (i=0; i<len; i++)
	{
		lis[i] = 1;
		for (j=0; j<i; j++)
		{
			if (str[j] < str[i] && lis[j] + 1 > lis[i])
			{
				lis[i] = lis[j] + 1;
			}
		}
	}
	for (i=0; i<len; i++)
	{
		if (lis[i] > max)
			max = lis[i];
	}

	return max;
}

这个问题还可以优化到O(nlgn),以后补充。

7,求最大子串和

这是最经典最常见的一道题,这里直接给出代码,代码中加了两个变量来标记最大子串的起始位置。

int SubMaxSum(int a[], int n) //子数组最大和
{
	int sum=0;
	int max=0;
	
	int begin, end;
	int temp = 0;
	
	for (int i=0; i<n; i++)
	{
		if (sum + a[i] < 0)
		{
			sum = 0;
			temp = i+1;
		}
		else
		{
			sum += a[i];
			if (sum > max)
			{
				max = sum;			
				begin = temp;	
				end = i;
			}
		}
	}
	
	for (i=begin; i<=end; i++)
		cout << a[i] << " ";
	cout << endl;
	
	return max;
}
8,求最大子串积
见这篇博文: http://blog.csdn.net/cl512976287/article/details/21337185
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值