UVa Problem 10069 Distinct Subsequences (不同的子序列)

// Distinct Subsequences (不同的子序列)
// PC/UVa IDs: 111102/10069, Popularity: B, Success rate: average Level: 3
// Verdict: Accepted
// Submission Date: 2011-10-10
// UVa Run Time: 0.026s
//
// 版权所有(C)2011,邱秋。metaphysis # yeah dot net
//
// [Problem Description]
// A subsequence of a given sequence S consists of S with zero or more elements
// deleted. Formally, a sequence Z = z1 z2 . . . zk is a subsequence of X = x1
// x2 . . . xm if there exists a strictly increasing sequence < i1 , i2 , . . . ,
// ik > of indices of X such that for all j = 1, 2, . . . , k, we have xij = zj.
// For example, Z = bcdb is a subsequence of X = abcbdab with corresponding index
// sequence < 2, 3, 5, 7 >.
//
// Your job is to write a program that counts the number of occurrences of Z in
// X as a subsequence such that each has a distinct index sequence.
//
// [Input]
// The first line of the input contains an integer N indicating the number of test
// cases to follow. The first line of each test case contains a string X, composed
// entirely of lowercase alphabetic characters and having length no greater than
// 10,000. The second line contains another string Z having length no greater than
// 100 and also composed of only lowercase alphabetic characters. Be assured that
// neither Z nor any prefix or suffix of Z will have more than 10^100 distinct
// occurrences in X as a subsequence.
//
// [Output]
// For each test case, output the number of distinct occurrences of Z in X as a
// subsequence. Output for each input set must be on a separate line.
//
// [Sample Input]
// 2
// babgbag
// bag
// rabbbit
// rabbit
//
// [Sample Output]
// 5
// 3
//
// [解题方法]
// 此题需要使用大数运算。使用一点 DP 即可。关键是如何得到递推关系,可以这样想,设母串的长度为 j,
// 子串的长度为 i,我们要求的就是长度为 i 的字串在长度为 j 的母串中出现的次数,设为 t[i][j],若
// 母串的最后一个字符与子串的最后一个字符不同,则长度为 i 的子串在长度为 j 的母串中出现的次数就是
// 母串的前 j - 1 个字符中子串出现的次数,即 t[i][j] = t[i][j - 1],若母串的最后一个字符与子
// 串的最后一个字符相同,那么除了前 j - 1 个字符出现字串的次数外,还要加上子串的前 i - 1 个字符
// 在母串的前 j - 1 个字符中出现的次数,即 t[i][j] = t[i][j - 1] + t[i - 1][j - 1]。

#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>

using namespace std;

#define MAXZ 101
 
class integer
{
	friend ostream& operator<<(ostream&, const integer&);
	
public:
	integer() { };
	
	// 将无符号整数转换为大整数。
	integer(unsigned int orginal)
	{
		do
		{
			digits.push_back(orginal % base);
			orginal /= base;
		} while (orginal);
	};
	
	~integer() { };

	integer operator+(const integer&);
	integer& operator+=(const integer&);
	
private:
	vector < unsigned int > digits;		// 数位。
	static unsigned int const base = 10000;	// 基数。
	static unsigned int const width = 4;	// 数位宽度。
};
	
// 重载输出符号 <<。
ostream& operator<<(ostream& os, const integer &number)
{
	os << number.digits[number.digits.size() - 1];
	for (int i = number.digits.size() - 2; i >= 0; i--)
		os << setw(number.width) << setfill('0') << number.digits[i];
	return os;
}

integer& integer::operator+=(const integer &b)
{
	return *this = *this + b;
}

// 加法。
integer integer::operator+(const integer &b)
{
	integer c;
	
	int carry = 0;
	for (int i = 0; i < digits.size() || i < b.digits.size() || carry; i++)
	{
		if (i < digits.size())
			carry += digits[i];
		if (i < b.digits.size())
			carry += b.digits[i];

		c.digits.push_back(carry % base);		
		carry /= base;
	}

	return c;
}

void distinctSubsequences(string x, string z)
{
	// 若为二维数组,可能因数据量多而会溢出导致段错误,故使用滚动数组来计算节省空间。
	// 当计算 t[i][j] 时,数组中保存的是 t[i][j - 1] 和 t[i - 1][j - 1] 的数据。
	integer occurrences[MAXZ];

	int xLength = x.length();
	int zLength = z.length();

	// 为 DP 准备初始条件。长度为 0 的子串在长度为 0 的母串中出现次数为 1,长度不为 0 的
	// 子串在长度为 0 的母串中出现次数为 0。
	occurrences[0] = integer(1);	
	for (int i = 1; i <= zLength; i++)
		occurrences[i] = integer(0);

	// DP 求出现次数。
	for (int i = 1; i <= xLength; i++)
		for (int j = zLength; j >= max(int(zLength - xLength + i), 1); j--)
			if (x[i - 1] == z[j - 1])
				occurrences[j] += occurrences[j - 1];

	cout << occurrences[z.length()] << endl;
}

int main(int ac, char *av[])
{
	string x, z;
	int cases;
	
	cin >> cases;
	while (cases--)
	{
		cin >> x >> z;
		
		if (x.length() < z.length())
			cout << 0 << endl;
		else
			distinctSubsequences(x, z);
	}

	return 0;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值