Atcoder agc027E

先考虑如何判定能否将字符串缩为单个字符。考虑将 a a a当做 1 1 1 b b b当做 2 2 2,这样每次操作后,总和在   m o d     3 \bmod \ 3 mod 3意义下不变,那么有解需要和在   m o d   3 \bmod 3 mod3意义下不为 0 0 0。并且若串长 > 1 >1 >1,需要有两个相邻的相同字符,容易归纳证明这个条件也是充要的,当存在两种字符时,每次操作一个 a b b abb abb类似的即可。
再考虑如何计数。我们发现匹配某个字符串的时候大致是贪心的,也即从右往左考虑模式串每个字符,尽可能的取原串最右边最短部分匹配。于是可以dp处理。但有一种特殊情况,考虑串 a b a b b ababb ababb a a aa aa匹配,如果贪心匹配,会用右边的 b b bb bb a a a匹配,此时左边的 a b a aba aba匹配不了 a a a,唯一合法选择是用右边的 b a b b babb babb a a a匹配。这种情况发生只可能出现在原串最左边的 a b a b a . . . ababa... ababa... b a b a b . . . babab... babab...后面接的一个段,特殊处理即可。
时间复杂度 O ( ∣ s ∣ ) \mathcal O(|s|) O(s)

#include 
#define MOD 1000000007
#define last last2

using namespace std;

char str[100005];
int f[100005],last[100005][3];

int main() {
  memset(last,255,sizeof(last));
  scanf("%s",str+1);
  int n=strlen(str+1);
  int nr=0;
  while (nr<n&&str[nr+1]!=str[nr]) nr++;
  int l=0,s=0;
  last[0][0]=0;
  for(int i=1;i<=n;i++) {
  	s=(s+str[i]-'a'+1)%3;
  	memcpy(last[i],last[i-1],sizeof(last[i]));
  	last[i][s]=i;
  	if (str[i]==str[i-1]) l=i-2;
  	int u=last[l][(s+2)%3],v=last[l][(s+1)%3];
  	if (str[i]=='a') u=i-1; else v=i-1;
  	if (u!=-1) {
	  f[i]=(f[i]+f[u])%MOD;
	  if (u&&nr>=u&&nr<i) f[i]=(f[i]+((u-1)>>1))%MOD;
    }
    if (v!=-1) {
	  f[i]=(f[i]+f[v])%MOD;
	  if (v&&nr>=v&&nr<i) f[i]=(f[i]+((v-1)>>1))%MOD;
	}
  	if (s&&(i==1||nr<i)) f[i]=(f[i]+1)%MOD;
  }
  printf("%d\n",f[n]);
  return 0;
}

你可能感兴趣的:(集训队作业,atcoder,动态规划)