链接
http://codeforces.com/contest/1005/problem/D
我的做法
先对整个数列
mod3
mod
3
直接贪心,从左往右切,如果发现以当前位置为结束点的某个子串的和模3等于
0
0
,就在这个数后面切
我记录一个数组表示以上一次切的位置的后面一个数字为起始点,到这个位置有没有和为
a[i]
a
[
i
]
的前缀,在记录当前前缀和,如果当前的前缀和存在,那说明可以切了,复杂度
O(n)
O
(
n
)
这个算法我不知道怎么证明,但是看了出题人的题解之后明白了
出题人的做法
出出题人用
dp
d
p
做,
z[i]
z
[
i
]
表示前缀
i
i
的答案
枚举以当前元素为结尾的子串,如果子串中数字和为
0
0
(),则
z[i]=z[j−1]+1
z
[
i
]
=
z
[
j
−
1
]
+
1
,取个最大值
但显然
z[i]
z
[
i
]
是单调不下降的,所以我只需要找到最大的
j
j
即可。所以再开一个数组记录前缀和为0
...3
.
.
.3
的最大的
j
j
,记录前缀和,每次转移就是
z[i]=z[fin[s[i]]]+1
z
[
i
]
=
z
[
f
i
n
[
s
[
i
]
]
]
+
1
,然后更新
fin[]
f
i
n
[
]
这个和我的算法不是一模一样吗?
代码
#include <cstdio>
#include <cstring>
#define maxn 200010
using namespace std;
char s[maxn];
int n, cnt, last, t;
bool exist[5];
int main()
{
int i, j;
scanf("%s",s+1);
n=strlen(s+1);
for(i=1;i<=n;i++)s[i]=(s[i]-48)%3;
exist[0]=1;
for(i=1;i<=n;i++)
{
t=(t+s[i])%3;
if(exist[t])
{
cnt++;
for(j=1;j<=2;j++)exist[j]=0;
t=0;
}
else exist[t]=1;
}
printf("%d",cnt);
return 0;
}