首页 > 代码库 > SPOJ_DSUBSEQ Distinct Subsequences
SPOJ_DSUBSEQ Distinct Subsequences
统计一个只由大写字母构成的字符串的,子串数目,这里指的是子串不是子序列,可以不连续,请注意
然后我按照计数DP的思想,dp[i][j]表示长度为i的子串,最后一个字母为j
然后为了去重,每一次dp的时候,记录这个时候最后一位所在的位数,而且之前用一个后缀记录之后有没有该字母,这样每次,从上一次的j所处的位置的下一个看看后缀有没有这个字母,合法的话 就 dp[i][j]+=dp[i-1][k]。
但是这个方法有个超大的漏洞,就是去重的问题,我为了防止重复,虽然用了后缀记录后面有没有这个字母,但是在加的时候,我是统一加的,即不管后面有没有字母,我直接统一+dp[i-1][j],导致结果到了后面就不行了,而且这个方法会超时,我当时虽然期待他超时的,没想到返回个WA了
其实可能是因为最近计数DP做的多了的结果,这个其实用一维就可以了,从前往后扫,对当前字母,我的值即为 dp[i-1]+添加当前字母之后产生的新子串
这个新子串的个数分两种情况,
1。该字母之前未出现过,则 新个数=dp[i-1]+1,表示当前字母加上后使得前面的dp[i-1]个串又能产生新的子串,+1是指自己本身单个字母。
2.该字母之前出现过,则要判断重复了,其实就是 dp[i-1]-dp[lastoccur-1];即先加上dp[i-1]但肯定是有重复的,为了去重,找到上次出现该字母的那个地方,减去那个地方就可以去重了
要注意的是,这个里面出现了减法,而答案是要取模的,所以这种相减是可能出现负数的情况的(这个地方确实之前没想到,没注意,我还纳闷怎么其他人都有个判断负值的操作),就是因为取模里面有减法,所以是可能出现负数的,注意这种情况
#include <cstdio>#include <iostream>#include <cstring>#define LL long longusing namespace std;const int N = 100010;const LL M = 1000000007;char str[N];LL dp[N];int lasts[30];int main(){ int t; scanf("%d",&t); while (t--) { scanf("%s",str+1); int len=strlen(str+1); for (int i=0;i<=len;i++){ dp[i]=0; } for (int i=0;i<30;i++) lasts[i]=0; for (int i=1;i<=len;i++){ dp[i]=dp[i-1]; if (lasts[str[i]-‘A‘]>0){ dp[i]+=dp[i-1]-dp[lasts[str[i]-‘A‘]-1]; while (dp[i]<0) dp[i]+=M; } else{ dp[i]+=dp[i-1]+1; } lasts[str[i]-‘A‘]=i; if (dp[i]>=M) dp[i]%=M; } dp[len]++; dp[len]%=M; printf("%lld\n",dp[len]); } return 0;}
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。