作者推荐
【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径
本文涉及知识点
LeetCode940. 不同的子序列 II
给定一个字符串 s,计算 s 的 不同非空子序列 的个数。因为结果可能很大,所以返回答案需要对 10^9 + 7 取余 。
字符串的 子序列 是经由原字符串删除一些(也可能不删除)字符但不改变剩余字符相对位置的一个新字符串。
例如,“ace” 是 “abcde” 的一个子序列,但 “aec” 不是。
示例 1:
输入:s = “abc”
输出:7
解释:7 个不同的子序列分别是 “a”, “b”, “c”, “ab”, “ac”, “bc”, 以及 “abc”。
示例 2:
输入:s = “aba”
输出:6
解释:6 个不同的子序列分别是 “a”, “b”, “ab”, “ba”, “aa” 以及 “aba”。
示例 3:
输入:s = “aaa”
输出:3
解释:3 个不同的子序列分别是 “a”, “aa” 以及 “aaa”。
参数范围
1 <= s.length <= 2000
s 仅由小写英文字母组成
动态规划
动态规划的状态表示
pre[j]表示前i个字符,以’a’+j 结尾的字符数量。dp[j]表示前i+1个字符,以’a’+j 结尾的字符数量。
动态规划的转移方程
{ 处理 i = 0 26 d p [ j ] + = p r e [ j ] 不选择 s [ i ] ,情况一 d p [ s [ i ] − ′ a ′ ] + = ∑ i = 0 26 p r e [ i ] + 1 , 选择 s [ i ] ,情况二 d p [ s [ i ] − ′ a ′ ] − = p r e [ s [ i ] − ′ a ′ ] 去掉重复 \begin{cases} 处理 \Large^{26}_{i=0} dp[j] += pre[j] & 不选择s[i] ,情况一\\ dp[s[i]-'a']+= \sum\Large_{i=0}^{26}pre[i] +1, & 选择s[i],情况二 \\ dp[s[i]-'a'] -= pre[s[i]-'a'] & 去掉重复 \end{cases}⎩⎨⎧处理i=026dp[j]+=pre[j]dp[s[i]−′a′]+=∑i=026pre[i]+1,dp[s[i]−′a′]−=pre[s[i]−′a′]不选择s[i],情况一选择s[i],情况二去掉重复
情况一和情况二内部不会重复。结束字符不同不会重复,故只需要考虑结束字符相同。
任意 pre[s[i]-‘a’] 去掉最后一个字符换成s[i],都是合法的情况二。→ \rightarrow→ 结束字符相同的情况一,全部重复,排除。
选择的情况不能直接2i,否则会有重复。 那个1表示空串。
动态规划的填表顺序
i从1到大
动态规划的初始值
pre[s[0]-‘a’]=1,其它为0。
动态规划的返回值
∑ i = 0 26 \sum\Large_{i=0}^{26}∑i=026pre[i]
代码
核心代码
template<int MOD = 1000000007> class C1097Int { public: C1097Int(long long llData = 0) :m_iData(llData% MOD) { } C1097Int operator+(const C1097Int& o)const { return C1097Int(((long long)m_iData + o.m_iData) % MOD); } C1097Int& operator+=(const C1097Int& o) { m_iData = ((long long)m_iData + o.m_iData) % MOD; return *this; } C1097Int& operator-=(const C1097Int& o) { m_iData = (m_iData + MOD - o.m_iData) % MOD; return *this; } C1097Int operator-(const C1097Int& o) { return C1097Int((m_iData + MOD - o.m_iData) % MOD); } C1097Int operator*(const C1097Int& o)const { return((long long)m_iData * o.m_iData) % MOD; } C1097Int& operator*=(const C1097Int& o) { m_iData = ((long long)m_iData * o.m_iData) % MOD; return *this; } bool operator<(const C1097Int& o)const { return m_iData < o.m_iData; } C1097Int pow(long long n)const { C1097Int iRet = 1, iCur = *this; while (n) { if (n & 1) { iRet *= iCur; } iCur *= iCur; n >>= 1; } return iRet; } C1097Int PowNegative1()const { return pow(MOD - 2); } int ToInt()const { return m_iData; } private: int m_iData = 0;; }; class Solution { public: int distinctSubseqII(string s) { vector<C1097Int<>> pre(26); pre[s.front() - 'a'] = 1; for (int i = 1; i < s.length(); i++) { vector<C1097Int<>> dp(26); C1097Int<> total = std::accumulate(pre.begin(), pre.end(), C1097Int<>(1)); for (int j = 0; j < 26; j++) { if ('a' + j != s[i]) { dp[j] += pre[j]; } else { dp[j] += total; } } pre.swap(dp); } return std::accumulate(pre.begin(), pre.end(), C1097Int<>()).ToInt(); } };
测试用例
template<class T> void Assert(const T& t1, const T& t2) { assert(t1 == t2); } template<class T> void Assert(const vector<T>& v1, const vector<T>& v2) { if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i < v1.size(); i++) { Assert(v1[i], v2[i]); } } int main() { string s; { Solution sln; s = "abc"; auto res = sln.distinctSubseqII(s); Assert(res, 7); } { Solution sln; s = "aba"; auto res = sln.distinctSubseqII(s); Assert(res, 6); } { Solution sln; s = "aaa"; auto res = sln.distinctSubseqII(s); Assert(res, 3); } { Solution sln; s = "adddddddddddddddddddddddddd"; auto res = sln.distinctSubseqII(s); Assert(res, 53); } { Solution sln; s = "ddddddddcdddddddfdddddddddedddddddddddddddd"; auto res = sln.distinctSubseqII(s); Assert(res, 20611); } { Solution sln; s = "abcdefghijklmnopqrstuvwxyzzzzaaa"; auto res = sln.distinctSubseqII(s); Assert(res, 671088636); } }
2023年1月
class C1097Int
{
public:
C1097Int(int iData = 0) :m_iData(iData)
{
} C1097Int operator+(const C1097Int& o)const { return C1097Int((m_iData + o.m_iData) % s_iMod); } C1097Int& operator+=(const C1097Int& o) { m_iData = (m_iData + o.m_iData) % s_iMod; return *this; } C1097Int operator*(const C1097Int& o)const { return((long long)m_iData *o.m_iData) % s_iMod; } C1097Int& operator*=(const C1097Int& o) { m_iData =((long long)m_iData *o.m_iData) % s_iMod; return *this; } int ToInt()const { return m_iData; }
private:
int m_iData = 0;;
static const int s_iMod = 1000000007;
};
int operator+(int iData, const C1097Int& int1097)
{
int iRet = int1097.operator+(C1097Int(iData)).ToInt();
return iRet;
}
int& operator+=(int& iData, const C1097Int& int1097)
{
iData = int1097.operator+(C1097Int(iData)).ToInt();
return iData;
}
class Solution {
public:
int distinctSubseqII(string s) {
m_resutl.resize(26);
for (int i = 0; i < 26; i++)
{
m_resutl[i].assign(s.length() + 1, -1);
}
C1097Int ret = 0;
for (char ch = ‘a’; ch <= ‘z’; ch++)
{
ret += Rev(0, s, ch);
}
return ret.ToInt();
}
C1097Int Rev(int iBegin, const string& s,const char beginChar)
{
int& iResult = m_resutl[beginChar - ‘a’][iBegin];
if (-1 != iResult)
{
return iResult;
}
for (; (iBegin < s.length()) && (beginChar != s[iBegin]); iBegin++);
if (s.length() == iBegin)
{
return iResult=0;
}
C1097Int ret =1 ;
for (char ch = ‘a’; ch <= ‘z’; ch++)
{
ret += Rev(iBegin + 1, s, ch);
}
return iResult = ret.ToInt();
}
vector<vector> m_resutl;
};