题解 P7114 【字符串匹配(洛谷民间数据)】

· · 题解

闲话:考场上想到了 \texttt{100pts} 的思路,然后自己想多了加了个不必要的优化然后 WA 到样例过不了无奈改暴力。(在T2上用了正解的时间,得到了暴力<可能都没有>的分数)

题意

--- #### 题解 $~~~~$首先,我们枚举 $AB$ 的长度,然后我们可以通过 $\texttt{Hash}$ 在 $\Theta(\sum_{i=2}^{len-1} \dfrac{len}{i})$ 的时间复杂度内算出每次重复了多少次。而且这个东西非常大概率跑不满。 $~~~~$ 然后我们假设 $AB$ 重复了 $k$ 次,那么可以有 $0\backsim(k-1)$ 次 $AB$ 的重复放在后面的 $C$ 字符串里,而且我们发现 $AB$ 重复奇数次和偶数次对于 $C$ 的奇数次出现的字符数的贡献是相同的,而这个次数我们可以 $\mathcal{O(n)}$ 预处理后每次 $\mathcal{O(1)}$ 得到。 $~~~~$ 最后我们对于某一个重复奇数次的字符数量,我们可以用维护 $\leq$ 某个出现次数的 $AB$ 划分方案,那么这显然是一个动态前缀和,用树状数组,那么这部分的时间复杂度是 $\mathcal{O(\log 26)}$ ,每次动态更新时由于只多了一个字符,所以也只用更新一个 $\mathcal{O(\log 26)}$ 的时间复杂度。 $~~~~$ 以下是笔者的废话。 $~~~~$ 到这里这道题已经做完了,然后我考场上认为上面一个一个跳太低效了,而且数据范围给出的是 $|S|$ 在某个 $2$ 的幂以内,但却没有用到 $\mathcal{O(\log n)}$ (~~更主要的原因是我不会算第一个式子~~)所以我想到 倍增/二分出重复次数,那么对于一个某个字符串 $S$ ,设其 $\texttt{HASH}$ 值为 $h$ ,$base^{|S|}=p$ ,则其重复 $k$ 次的 $\texttt{HASH}$ 值应为 $\sum_{i=0}^{k-1} h\times p^i$ 然后一波等比数列求和可以算出来。事实上这是没有问题的,但我们要考虑到 $\texttt{Hash}$ 是由自然溢出/取模的,只是平时都不太容易溢出/取模次数不同步,但我们对大数作乘方运算后,它容易溢出不同步,然后因为精度原因 WA 掉。 $~~~~$ ~~然后我就改成 $len^2$ 暴力亲手送掉1= QAQ~~ #### 代码 ```cpp #include <cstdio> #include <vector> #include <cstring> #include <algorithm> #define ll long long #define ull unsigned long long using namespace std; const ull base=13331; int cnt[1500000],t[30]; char s[1500000]; ull Hash[1500000],p[1500000]; ll len; ll tr[30]; ull Get_Hash(ll l,ll r) { return Hash[r]-Hash[l-1]*p[r-l+1]; } inline ll lowbit(ll x){return x&(-x);} void clearTr(){memset(tr,0,sizeof(tr));} void add(ll x,ll val){for(;x<=26;x+=lowbit(x))tr[x]+=val;} ll query(ll x){ll ret=0;for(;x;x-=lowbit(x)) ret+=tr[x];return ret;} int main() { // freopen("data25.in","r",stdin); // freopen("data25.out","w",stdout); ll T; scanf("%lld",&T); while(T--) { memset(Hash,0,sizeof(Hash)); memset(cnt,0,sizeof(cnt)); clearTr(); for(ll i=0;i<=26;i++) t[i]=0; scanf("%s",s+1); len=strlen(s+1); p[0]=1; for(ll i=1;i<=len;i++) { Hash[i]=Hash[i-1]*base+s[i]-'a'+1; p[i]=p[i-1]*base; } for(ll i=0;i<=26;i++) t[i]=0; for(ll i=len;i>=1;i--) { t[s[i]-'a']++; if(t[s[i]-'a']&1) cnt[i]=cnt[i+1]+1; else cnt[i]=cnt[i+1]-1; } for(ll i=0;i<=26;i++) t[i]=0; t[s[1]-'a']++; add(2ll,1ll);//The position that can let the odd number of letters <= a number ll ans=0,last=1; for(ll i=2;i<len;i++)//The length of AB { ll now=1; ull tmp=Get_Hash(1,i); ll Sta=i+1,End=2*i; while(1) { if(Get_Hash(Sta,End)!=tmp||End>=len) break; else Sta+=i,End+=i,now++; } // if(End>len) now--; // if(now==0) now=1; ll K=now-1; if(K&1) { ans+=(1+(K-1)/2)*query(cnt[i*now+1]+1); ans+=(K+1)/2*query(cnt[i*(now-1)+1]+1); } else { ans+=(K/2+1)*query(cnt[i*now+1]+1); ans+=K/2*query(cnt[i*(now-1)+1]+1); } t[s[i]-'a']++; if(t[s[i]-'a']&1) last++; else last--; add(last+1,1); } printf("%lld\n",ans); } return 0; } ```