题目描述
读入一个字符串 SS, 只包含阿拉伯数字.
问 SS 有多少个回文子串 S[i,j]S[i,j], 使得 S[i,j]S[i,j] 构成一个不含前导零并且能被 33 整除的非负整数.
输入格式
输入只有一行, 包括一个字符串 SS, 仅包含阿拉伯数字.
输出格式
输出只有一个整数, 表示答案.
样例 1
输入
01001001000
输出
9
说明
在样例中, 含有 88 个 00, 以及一个 10010011001001.
如果 X X X 是个回文串, 记 p ( X ) = 1 p(X) = 1 p(X)=1, 否则 p ( X ) = 0 p(X) = 0 p(X)=0.
记 f ( i ) f(i) f(i) 为右端点为 i i i 的回文子串的个数. 即 f ( i ) = ∑ j = 1 i p ( S [ j , i ] ) f(i) = \sum_{j = 1}^i p(S[j, i]) f(i)=∑j=1ip(S[j,i]).
记 F ( i ) F(i) F(i) 为 f ( i ) f(i) f(i) 的前缀和, 即 F ( i ) = ∑ j = 1 i f ( i ) F(i) = \sum_{j=1}^i f(i) F(i)=∑j=1if(i).
那么如果一个回文子串是 S [ x , y ] S[x, y] S[x,y], 我们可以统计出在其左侧与其相交的回文串的个数为 F ( y ) − F ( x − 1 ) F(y) - F(x - 1) F(y)−F(x−1).
我们从被减数和减数两个方向去观察.
F ( x ) F(x) F(x) 作为被减数的次数, 其实就是 f ( x ) f(x) f(x).
F ( x ) F(x) F(x) 作为减数的次数, 其实是 ∑ i = x + 1 ∣ S ∣ p ( S [ x , i ] ) \sum_{i=x + 1}^{|S|} p(S[x, i]) ∑i=x+1∣S∣p(S[x,i]), 这其实是 S [ x , ∣ S ∣ ] S[x, |S|] S[x,∣S∣] 的回文前缀的个数, 把串倒过来求一边后缀树即可.
#include
#define int long long
using namespace std;
int read(){
int f=1,ans=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
return ans*f;
}
const int maxn=4000001;
char str[maxn];int N,A[maxn],S[maxn],yjy;
struct PAM{
int siz[maxn],len[maxn],S[maxn],fa[maxn],ch[maxn][27],tot,las,R[maxn];
bool f[maxn];
void Init(){tot=1,las=0;len[1]=-1,fa[0]=1;return;}
void ins(int c,int N){
int u=las;while(str[N-len[u]-1]!=str[N]) u=fa[u];
if(!ch[u][c]){
int nw=++tot,v=fa[u];
while(str[N-len[v]-1]!=str[N]) v=fa[v];
fa[nw]=ch[v][c],ch[u][c]=nw,len[nw]=len[u]+2;
}las=ch[u][c];siz[las]++;R[las]=N;
}
}T;
int Query(int l,int r){return S[r]-S[l-1];}
signed main(){
scanf("%s",str+1);N=strlen(str+1);
T.Init();
for(int i=1;i<=N;i++) T.ins(str[i]-'0',i),A[i]=str[i]-'0',S[i]=S[i-1]+A[i];
for(int i=T.tot;i>=2;i--) T.siz[T.fa[i]]+=T.siz[i];
for(int i=2;i<=T.tot;i++) if(A[T.R[i]]!=0&&(Query(T.R[i]-T.len[i]+1,T.R[i])%3)==0){
yjy+=T.siz[i];
}
for(int i=1;i<=N;i++) yjy+=(A[i]==0);
printf("%lld\n",yjy);return 0;
}