答案 0 :(得分:58)
这是一个经典的动态编程问题。
让:
dp[i] = number of distinct subsequences ending with a[i]
sum[i] = dp[1] + dp[2] + ... + dp[i]. So sum[n] will be your answer.
last[i] = last position of character i in the given string.
空字符串有一个子序列,因此dp[0] = 1
。
read a
n = strlen(a)
for i = 1 to n
dp[i] = sum[i - 1] - sum[last[a[i]] - 1]
sum[i] = sum[i - 1] + dp[i]
last[a[i]] = i
return sum[n]
<强>解释强>
dp[i] = sum[i - 1] - sum[last[a[i]] - 1]
最初,我们假设我们可以将a[i]
附加到以前字符结尾的所有子序列,但这可能违反了计算的子序列需要区分的条件。请记住,last[a[i]]
为我们提供了迄今为止出现的最后一个位置a[i]
。我们过度计算的唯一子序列是前一个a[i]
附加到的子序列,因此我们将其减去。
sum[i] = sum[i - 1] + dp[i]
last[a[i]] = i
根据其定义更新这些值。
如果索引从0开始,请在我使用a[i - 1]
的任何地方使用a[i]
。如果您要提交代码,请记住将计算包装在mod
函数中。这应该像这样实现:
mod(x) = (x % m + m) % m
为了正确处理某些语言中的负值(例如C / C ++)。
答案 1 :(得分:30)
这个问题有一个更简单的解决方案。
这个想法是:如果字符串的所有字符都是不同的,则子序列的总数是2^n.
现在,如果我们发现之前已经发生的任何字符,我们应该只考虑它的最后一次出现(否则序列获胜)不明白。因此,我们必须减去由于之前发生的子序列的数量。
我的实现是这样的:
read s
dp[0] = 1
len = strlen(s)
last[s.length()] = {-1} //declaring `last` array with same as length of string `s` and all elements initialized with -1.
for (i = 1; i <= len; i++)
{
dp[i] = (dp[i - 1] * 2)
if (last[s[i]] > 0) dp[i] = (dp[i] - dp[last[s[i]] - 1])
last[s[i]] = i
}
答案 2 :(得分:0)
这是我的 CODE :
#include<iostream>
typedef long long ll;
ll fun(std::string s,ll visited[256],ll n,ll L[]){
ll ans=0;
if(n<0){
return 1;
}
//std::cout<<s.substr(0,n+1)<<" "<<n<<endl;
ans=fun(s,visited,n-1,L);
L[n]=ans;
ans=ans*2;
if(visited[int(s[n])]>=0){
ans -= L[visited[int(s[n])]];
}
visited[int(s[n])]=n;
return ans;
}
int main(){
std::string s;
std::cin>>s;
ll n=s.length();
ll visited[256];
ll L[n];
memset(visited,-1,sizeof(visited));
memset(L,-1,sizeof(L));
std::cout<<fun(s,visited,n-1,L);
return 0;
}
说明:
我从字符串的末尾(即从最后一个元素到第一个元素)进行扫描,因此发送前n-1
个字符以进行递归的进一步扫描。
一旦n==-1 or n<0(both are same)
,我到达空字符串并返回1,因为没有。空字符串的子序列的数量为1。
因此,从递归返回时,我们知道将当前的非重复字符添加到前一个字符串将使no翻倍。子序列。之所以会加倍,是因为现在我可以在所有先前子序列的末尾添加此字符。因此,with
和without
这个字符表示所有先前子序列的两倍。
假设当前字符不是重复字符,我将前一个字符乘以。 2的子序列。
总数之后已计算出前n-1
个字符的子序列的数量,我们将它们对前n
个字符进行了双倍处理。
但是,假设当前遇到的字符(第n个字符)已经出现在前面的前n-1
个字符中(即-在字符串s [0 .... n-1]中找到(注:s [n]是当前字符)),那么我们必须减去那些数字。上次遇到此当前字符时,s的最高部分(不包括s)的子序列,并且已经计算并存储在L ['这个特定字符']中。
ie-BACA
-对于给定的字符串,之前已经遇到了第四个A
(从递归返回时,我们首先遇到B
,然后是A
,然后是C
,最后是A
),因此我们减去了。最多计算(不包括)第二个A
(为2(因为A
之前的子序列数为2)的子序列)。
因此,每次我们计算出no。前n-1
个字符的子序列,我们将它们存储在数组L中。
通知:L [k]存储编号。第k个索引之前的子序列的数量。
我使用了访问数组来检查我当前所在的给定字符是否已被扫描。
遇到当前字符时,我用当前位置的位置更新为n
的访问数组。这样做是因为我们必须排除重复的序列。
注意:visited[]
用全-1初始化,因为字符串s
中任何字符的位置都不为负(基于0的索引)。
摘要:
How do you arrive at the number of duplicates? Let's say the last occurrence of current character at i, was at j'th position. Then, we will have duplicate subsequences: consider starting with i'th character and then all subsequences possible from [0,j-1] vs. starting at j'th character and then all subsequences possible from [0,j-1]. So, to eliminate this, you subtract the number of subsequences possible from upto (excluding) j with L[0]=1 mean that upto(excluding 0), no. of subseq are 1(empty string has 1 subsequence).
答案 3 :(得分:-2)
///i get wa
int finding_dist_subs(int len,char data[])
{
dp[0]=1;
for(int i=1;i<len;i++)
{
dp[i]=(dp[i-1]*2+1)%1000000007;
for(int j=i-1;j>=0;j--)
{
if(data[i]==data[j])
{
if(j!=0)
dp[i]=(dp[i]-(dp[j-1])-1)%1000000007;
else dp[i]=(dp[i]-1)%1000000007;
break;
}
}
}
return dp[len-1];
}