【学习笔记】CF1383E Strange Operation

被这道题搞得有点自闭,最后发现计数的地方还是没想清楚。

仔细想一想 如何判断一个字符串 T T T能否被生成。为什么要说仔细想一想,因为 1 1 1的连续段是不能随便删的。然后发现可以将 T T T分段,如果在 T T T的末尾插入一段 0 0 0,那么要求此时 S S S对应匹配的那个位置也是 0 0 0,并且 0 0 0的连续段的长度要更长;如果在 T T T的末尾插入一段 1 1 1,那么注意此时不是截取 S S S中最短的前缀去获得,而是要保证此时 S S S中以 0 0 0开头并且长度足够,在此条件下去截取最短前缀,这是我们在前面已经分析过的。于是我们的一个初步感知是 0 0 0 1 1 1要分开讨论,并且因为是类似 D F A DFA DFA的匹配过程所以 D P DP DP状态是极易的,转移实际上也并不困难,我们不妨细致讨论一下。

显然倒着 D P DP DP更容易处理。设 p i p_i pi表示以 i i i开头前缀 0 0 0的长度, d p z e r o [ i ] dpzero[i] dpzero[i]表示 [ i , n ] [i,n] [i,n]能生成的以 0 0 0开头的字符串的数目,先不管是否包含空串。同理设 d p o n e [ i ] dpone[i] dpone[i]表示 [ i , n ] [i,n] [i,n]能生成的以 1 1 1开头的字符串数目。这样设是为了满足 01 01 01连续段交替的条件。注意这里的定义是 i i i必须选 。并且为了保证不算重,我们规定 d p z e r o [ i ] dpzero[i] dpzero[i] 必须把这一段 0 0 0全部选完,并且后面必须接上至少一个 1 1 1。同时设 l i l_i li表示前缀 0 0 0长度 ≥ i \ge i i的最小的位置。因为 D P DP DP转移并不复杂(这是可以预见的),所以就一边维护信息一边转移就好了。分类讨论:

1.1 1.1 1.1 S [ i ] = 0 S[i]=0 S[i]=0,根据定义有 d p z e r o [ i ] ← d p o n e [ i + p [ i ] ] dpzero[i]\gets dpone[i+p[i]] dpzero[i]dpone[i+p[i]]

1.2 1.2 1.2 S [ i ] = 1 S[i]=1 S[i]=1,考虑替换的思想,设 [ i + 1 , n ] [i+1,n] [i+1,n]能生成串 a a a,那么 1 a ‾ \overline{1a} 1a显然也能被生成,因此有 d p o n e [ i ] ← d p o n e [ i + 1 + l [ i + 1 ] ] dpone[i]\gets dpone[i+1+l[i+1]] dpone[i]dpone[i+1+l[i+1]],但是漏算了 a a a 0 0 0开头的情形,也就是 d p o n e [ i ] ← ∑ d p z e r o [ l [ j ] ] dpone[i]\gets \sum dpzero[l[j]] dpone[i]dpzero[l[j]]。注意还有一种情况是 a a a全为 0 0 0,设末尾连续 0 0 0的长度为 n u m num num,那么 d p o n e [ i ] ← n u m + 1 dpone[i]\gets num+1 dpone[i]num+1

发现可以用数组 O ( 1 ) O(1) O(1)维护,然后就做完了。

最后统计答案的时候也注意不要搞错了。如果写一遍的时候就能把这些细节全部想清楚的话那我觉得真是太酷拉。

复杂度 O ( n ) O(n) O(n)

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define db double #define inf 0x3f3f3f3f3f3f3f3f using namespace std; const int mod=1e9+7; const int N=1e6+5; int n,zeros,p[N]; ll dpzero[N],dpone[N],dp[N],sumzero,res; string s; void add(ll &x,ll y){x=(x+y)%mod;} signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>s,n=s.size(); for(int i=n-1;i>=0;i--){ if(s[i]=='0'){ p[i]=p[i+1]+1; } } int n2=n-1;while(n2>=0&&s[n2]=='0')n2--; if(n2<0){ cout<<n; return 0; } for(int i=n2;i>=0;i--){ if(s[i]=='0'){ add(dpzero[i],dpone[i+p[i]]); add(sumzero,-dp[p[i]]); add(sumzero,dpzero[i]); dp[p[i]]=dpzero[i]; } else{ add(dpone[i],dpone[i+1+p[i+1]]+sumzero+n-n2); } } for(int i=p[0];i>=0;i--){ if(s[i]=='1'){ add(res,dpone[i]); } else{ add(res,dpzero[i]); } } cout<<(res+mod)%mod; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529955.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示