leetcode10:Distinct Subsequences

Distinct Subsequences

题目连接:https://oj.leetcode.com/problems/distinct-subsequences/

runtimes:38ms


一、问题

        Given a string S and a string T, count the number of distinct subsequences of T in S.

        A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).

       Here is an example:
       S = "rabbbit"T = "rabbit"

       Return 3.

二、分析

       如题所示,题目要求求出字符串S有多少个与字符串T相同的字符串。传统思路是用回溯法,首先比较S[i]和T[i],如果相等,那么就继续比较S[i + 1]和T[i + 1];否则比较S[i + 1]和T[i],依次类推,直到扫描完T计数一次,扫描完S表明不存在相同序列。但是酱紫时间和空间消耗都很大,一般来说跑步过去。因此要另寻蹊径。

       想起以前算法教过的求两个字符串的最长子字符串,那么这道题应该相似,用动态规划的思想。


       如上图,以S = “rabbrabitbt”,T = "rabbit"为例,开一张表record[][]记录检查过的字符,递推关系如下:

      如果S[j] == T[i],record[i][j] = record[i][j - 1] + record[i - 1][j - 1]

      如果S[j] != T[i],record[i][j] = record[i][j - 1] 

      最终扫描结束,得到record[T.size() - 1][S.size() - 1]为所求结果。

      凡事有个为什么。首先T字符串每一个字符为一个阶段,该阶段reocrd[i][j]的决策受上个阶段的结果影响,record[i][j - 1]表示T[i]已经和S的前j - 1个字符串比较过了,如果不同,说明之前T[i]与S的前j个字符串至少有record[i][j - 1]个相同;如果相同,需要看上阶段字符record[i - 1][j - 1]的情况。

      有如下规定:

      假如T为空,则record[0][1...S.size() - 1]都为1,表示T和S只有一个子序列空相同;

      假如S为空,则record[1...T.size() - 1]都为零。

三、小结

      用一张二维表记录状态,递推公式如下:

     如果S[j] == T[i],record[i][j] = record[i][j - 1] + record[i - 1][j - 1]

      如果S[j] != T[i],record[i][j] = record[i][j - 1] 

四、实现

回溯方案的实现:

class Solution {
public:
	void find(string S, string T, int sk, int tk, int &counter)
	{
		if (tk < T.size())
		{
			for (int i = sk; i < S.size(); i++)
			{
				if (S[i] == T[tk])
				{
					find(S, T, i + 1, tk + 1, counter);
				}
			}

		}
		else{
			counter++;
		}
	}

	void find2(vector <vector <int> > v, int t, int k, int n, int &counter)
	{
		if (k < n)
		{
			for (int i = 0; i < v[k].size(); i++)
			{
				if (t < v[k][i])
				{
					find2(v, v[k][i], k + 1, n, counter);
				}
			}
		}
		else{
			counter++;
		}
	}

	int numDistinct(string S, string T) {
		int c = 0;
		vector <vector <int> > vec;
		for (int i = 0; i < T.size(); i++)
		{
			for (int j = 0; j < S.size(); j++)
			{
				if (T[i] == S[j])
				{
					if (i >((int)vec.size() - 1))
					{
						vector <int> v;
						v.push_back(j);
						vec.push_back(v);
					}
					else{
						vec[i].push_back(j);
					}
				}
			}
		}
		find2(vec, -1, 0, vec.size(), c);
		cout << c;
		return c;
	}
};


动态规划方案的实现:

class Solution {
public:
	int numDistinct(string S, string T) {
		vector <vector <int> > record(T.size() + 1, vector <int>(S.size() + 1, 0));
		for (int j = 0; j <= T.size(); j++)
			record[j][0] = 0;
		for (int i = 0; i <= S.size(); i++)
			record[0][i] = 1;
		for (int i = 1; i <= T.size(); i++)
		{
			for (int j = 1; j <= S.size(); j++)
			{
				record[i][j] = T[i - 1] == S[j - 1] ? record[i][j - 1] + record[i - 1][j - 1] : record[i][j - 1];
			}
		}
		return record[T.size()][S.size()];
	}
};


五、三思

       这个算法的思想参考了以下文章,虽然自己知道最长子序列问题,但是还是不能够灵活的转换运用,动态规划确实比较难,灵活性高,只能多实践多认识。其次发现leetcode的数据有时候会比较长,那么用vector作为数组就方便多了,不会出现奇葩的错误。加油!

       原文链接:http://blog.csdn.net/abcbc/article/details/8978146

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值