用于求数列的最短递推式。
本文参考自 https://www.cnblogs.com/jz-597/p/14983564.html。
增量法,设 R i R_i Ri 表示第 i i i 个历史递推式,当前为 R c n t R_{cnt} Rcnt。
设 Δ \Delta Δ 表示真实的 a i a_i ai 与用 R c n t R_{cnt} Rcnt 求出的 a i a_i ai 的差值,即 Δ = a i − ∑ j = 1 ∣ R c n t ∣ a i − j R c n t , j \Delta=a_i-\sum_{j=1}^{|R_{cnt}|}a_{i-j}R_{cnt,j} Δ=ai−∑j=1∣Rcnt∣ai−jRcnt,j。
- 若 Δ = 0 \Delta=0 Δ=0,则无事发生,跳过。
- 若 Δ ≠ 0 \Delta\neq 0 Δ=0,那么我们需要调整这个递推式使得它对 a i a_i ai 也成立。
接下来看第二种情况怎么处理。
有一个想法是找到另一个递推式,使得这个递推式在算 ⋯ , a i − 1 \cdots,a_{i-1} ⋯,ai−1 时取 0 0 0,在算 a i a_i ai 时取 Δ \Delta Δ。
我们可以找到一个历史递推式 R l s t R_{lst} Rlst,设其失配位置为 f a i l l s t fail_{lst} faillst,失配时的差值为 Δ l s t \Delta_{lst} Δlst(显然 Δ l s t ≠ 0 \Delta_{lst}\neq 0 Δlst=0)。
设 d = Δ Δ l s t d=\dfrac{\Delta}{\Delta_{lst}} d=ΔlstΔ,那么我们可以找到这么一个递推式 R ′ = { 0 , 0 , ⋯ , 0 , d , − d ⋅ R l s t , 1 , − d ⋅ R l s t , 2 , ⋯ } R'=\{0,0,\cdots,0,d,-d\cdot R_{lst,1},-d\cdot R_{lst,2},\cdots\} R′={0,0,⋯,0,d,−d⋅Rlst,1,−d⋅Rlst,2,⋯},其中前面是 i − f a i l l s t − 1 i-fail_{lst}-1 i−faillst−1 个 0 0 0。
可以发现,使用这个递推式得到的 a i ′ = ∑ j = 1 ∣ R ′ ∣ R j ′ a i − j = d ( a f a i l l s t − ∑ j = 1 ∣ R l s t ∣ R l s t , j a f a i l l s t − j ) = d ⋅ Δ l s t = Δ a_i'=\sum_{j=1}^{|R'|}R'_{j}a_{i-j}=d(a_{fail_{lst}}-\sum_{j=1}^{|R_{lst}|}R_{lst,j}a_{fail_{lst}-j})=d\cdot \Delta_{lst}=\Delta ai′=∑j=1∣R′∣Rj′ai−j=d(afaillst−∑j=1∣Rlst∣Rlst,jafaillst−j)=d⋅Δlst=Δ。
而对于任意 ( i − k ) ∈ [ ∣ R ′ ∣ + 1 , i − 1 ] (i-k)\in [|R'|+1,i-1] (i−k)∈[∣R′∣+1,i−1], a i − k ′ = ∑ j = 1 ∣ R ′ ∣ R j ′ a ( i − k ) − j = d ( a f a i l l s t − k − ∑ j = 1 ∣ R l s t ∣ R l s t , j a ( f a i l l s t − k ) − j ) = 0 a_{i-k}'=\sum_{j=1}^{|R'|}R'_j{a_{(i-k)-j}}=d(a_{fail_{lst}-k}-\sum_{j=1}^{|R_{lst}|}R_{lst,j}a_{(fail_{lst}-k)-j})=0 ai−k′=∑j=1∣R′∣Rj′a(i−k)−j=d(afaillst−k−∑j=1∣Rlst∣Rlst,ja(faillst−k)−j)=0。
那么我们令 R c n t + 1 ← R c n t + R ′ R_{cnt+1}\gets R_{cnt}+R' Rcnt+1←Rcnt+R′ 即可。
实质上是利用了以前的信息 R l s t R_{lst} Rlst:注意到失配前的差值都是 0 0 0,所以我们用差值来定义递推式。
那么我们怎么找到最短的 R ′ R' R′ 呢?由于 ∣ R ′ ∣ = i + ( ∣ R l s t ∣ − f a i l l s t ) |R'|=i+\big(|R_{lst}|-fail_{lst}\big) ∣R′∣=i+(∣Rlst∣−faillst),所以我们直接找 ∣ R l s t ∣ − f a i l l s t |R_{lst}|-fail_{lst} ∣Rlst∣−faillst 最小的就好了。
至于为什么求出来的递推式一定是最短的不会证。
【XSY3403】求数列的最短递推式 代码:
#include<bits/stdc++.h>
#define N 10010
using namespace std;
namespace modular
{
const int mod=1000000007;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
}using namespace modular;
inline int poww(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,a[N],delta[N],fail[N];
vector<int>r[N];
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read();
int cnt=0,lst=cnt;
for(int i=1;i<=n;i++)
{
delta[cnt]=0;
for(int j=0;j<r[cnt].size();j++)
Add(delta[cnt],mul(r[cnt][j],a[i-j-1]));
delta[cnt]=dec(a[i],delta[cnt]);
if(!delta[cnt]) continue;
fail[cnt]=i;
if(!cnt)
{
r[++cnt].resize(i);
continue;
}
r[cnt+1]=r[cnt];
r[cnt+1].resize(max((int)r[cnt+1].size(),i+(int)r[lst].size()-fail[lst]));
int d=mul(delta[cnt],poww(delta[lst],mod-2));
Add(r[cnt+1][i-fail[lst]-1],d);
for(int j=0;j<r[lst].size();j++)
Add(r[cnt+1][i-fail[lst]+j],mul(dec(0,d),r[lst][j]));
if((int)r[cnt].size()-fail[cnt]<(int)r[lst].size()-fail[lst]) lst=cnt;
cnt++;
}
printf("%d\n1000000006 ",(int)r[cnt].size());
for(int i=0;i<r[cnt].size();i++)
cout<<r[cnt][i]<<" \0"[i==r[cnt].size()-1];
return 0;
}