- 有一个初始全为(0)的数组一个初始在(0)的指针。
- 给定一个长度为(n)的操作串,有四种操作符:"<"表示将指针左移一位,">"表示将指针右移一位,"+"表示将指针对应位置加(1),"-"表示将指针对应位置减(1)。
- 求有多少子串,使得执行其中操作得到的数组和整串相同。
- (nle2.5 imes10^5)
哈希
我们设(f_i(x))表示执行了前(i)位得到数组的生成函数(sum_{k=-infty}^{+infty}a_kx^k),(g_i(x))表示执行了前(i)位指针指向位置(p_i)对应的(x^{p_i})。
考虑一个操作符的转移。
如果是"<"或">",则(f_i(x)=f_{i-1}(x)),(g_i(x)=g_i(x) imes x^{mp1})。
如果是"+"或"-",则(f_i(x)=f_{i-1}(x)pm g_i(x)),(g_i(x)=g_{i-1}(x))。
然后我们只要任选两个(x)代入,就成为双哈希了。
答案的计算
考虑一段([L,R])操作得到的生成函数应该是:
[frac{f_R(x)-f_{L-1}(x)}{g_{l-1}(x)}
]
现在它需要等于(f_n(x)),也就是说:
[f_n(x)=frac{f_R(x)-f_{L-1}(x)}{g_{l-1}(x)}Leftrightarrow f_n(x) imes g_{l-1}(x)+f_{L-1}(x)=f_R(x)
]
因此我们从后往前枚举(L),(map)中维护好所有的(f_R(x)),然后询问有多少个$ f_n(x) imes g_{l-1}(x)+f_{L-1}(x)$计入答案即可。
代码:(O(nlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 250000
#define S1 324682339
#define S2 456789001
#define X 998244353
using namespace std;
int n;char s[N+5];I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
struct Hash
{
int x,y;I Hash() {x=y=0;}I Hash(CI a):x(a),y(a){}I Hash(CI a,CI b):x(a),y(b){}
I Hash operator + (Con Hash& o) Con {return Hash((x+o.x)%X,(y+o.y)%X);}
I Hash operator - (Con Hash& o) Con {return Hash((x-o.x+X)%X,(y-o.y+X)%X);}
I Hash operator * (Con Hash& o) Con {return Hash(1LL*x*o.x%X,1LL*y*o.y%X);}
I bool operator < (Con Hash& o) Con {return x^o.x?x<o.x:y<o.y;}
}seed,f[N+5],g[N+5],sd(S1,S2),isd(QP(S1,X-2),QP(S2,X-2));map<Hash,int> p;
int main()
{
RI i;for(scanf("%d%s",&n,s+1),g[0]=i=1;i<=n;++i) switch(s[i])//按运算符分类讨论
{
case '+':f[i]=f[i-1]+(g[i]=g[i-1]);break;case '-':f[i]=f[i-1]-(g[i]=g[i-1]);break;
case '<':f[i]=f[i-1],g[i]=g[i-1]*isd;break;case '>':f[i]=f[i-1],g[i]=g[i-1]*sd;break;
}
long long t=0;for(i=n;i;--i) ++p[f[i]],t+=p[g[i-1]*f[n]+f[i-1]];return printf("%lld
",t),0;//从后往前枚举L统计答案
}