传送门:3-Palindromes
题意:求为回文串且能整除3且不前导0的子串个数。
分析:由 manacher算法O(N)可算出以i为坐标的最长为p[i]回文子串,且Si-k,Si-k+1......Si+k-1,Si+k(0<k<p[i])全为回文串。
又知,能整除3的整数数位和也能整除3,那么只要Si-k,Si-k+1......Si+k-1,Si+k和整除3即可。
由回文串对称性知Si-k==Si-k,那么只要Si-k..Si-1这段中模3余数与Si模3余数相同,Si-k...Si+k和必定整除3(设左右各位余数x+本身x=3x).
因此只要预处理出Si...Sj整段中模3余0,1,2的个数,就可O(N)得出全部符合条件的子串。
#pragma comment(linker,"/STACK:1024000000,1024000000") #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <limits.h> #include <iostream> #include <algorithm> #include <queue> #include <cstdlib> #include <stack> #include <vector> #include <set> #include <map> #define LL long long #define mod 1000000007 #define inf 0x3f3f3f3f #define eps 1e-6 #define N 1000010 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define PII pair<int,int> using namespace std; inline LL read() { char ch=getchar();LL x=0,f=1; while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();} return x*f; } int p[N<<1],len,num,mx,id; char s[N],str[N<<1]; void build() { len=strlen(s);num=0; str[num++]='@';str[num++]='#'; for(int i=0;i<len;i++) { str[num++]=s[i]; str[num++]='#'; } str[num]=0; } void manacher() { mx=0; memset(p,0,sizeof(p)); for(int i=1;i<num;i++) { if(mx>i)p[i]=min(p[2*id-i],mx-i); else p[i]=1; while(str[i-p[i]]==str[i+p[i]])p[i]++; if(p[i]+i>mx)mx=p[i]+i,id=i; } } int a[N<<1],sum[N<<1][3]; void solve() { for(int i=2;i<num;i++) { a[i]=a[i-1];//前缀和 if(str[i]!='#')a[i]=(a[i]+str[i]-'0')%3; for(int j=0;j<3;j++)sum[i][j]=sum[i-1][j]; if(str[i]!='#'&&str[i]!='0') sum[i][a[i]]++; } LL ans=0; for(int i=2;i<num;i++) { int t=(str[i]-'0')%3; if(str[i]=='#')t=0; if(str[i]!='#'&&t==0)ans++; int k=(t+a[i])%3;//由于sum[i+p[i]-1][k]~sum[i][k]都多了a[i],因此补回来防止误差 ans+=sum[i+p[i]-1][k]-sum[i][k]; } printf("%lld ",ans); } int main() { while(scanf("%s",s)>0) { build(); manacher(); solve(); } }