我对状态空间的理解:https://www.cnblogs.com/AKMer/p/9622590.html
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=5443
这题能在暴力美学分组中占压轴地位,是不简单的。我从未见过如此灵性的暴力题目(也许是我太弱了题目写少了)。
我们令(ans[j][i])表示与串([i,i+l-1])距离为(j)的串的个数,最暴力的方法显然是直接(O(n^2*l))去匹配的。
但是其中我们会对一些字符进行若干次重复的比较。比如([1,3])和([2,4])匹配时以及([2,4])和([3,5])都会判断(a[3])是否等于(a[3])。很显然这种冗余的操作是不优的。那么我们怎么减少这样的操作呢?
性质:如果我们已经知道了([l1,r1])与([l2,r2])的距离,那么我们可以在(O(1))时间内算出([l1+1,r1+1])与([l2+1,r2+1])的距离。设前者为(s),后者为(t),那么(t=s-(a[l1]!=a[l2])+(a[r1+1]!=a[r2+1]))。
也就是说,我们可以在(O(n))的时间内匹配完(n)条子串。
我们枚举一个(len),令(h1)为(1),(h2)为(h1+len),然后暴力匹配([h1,h1+l-1])和([h2,h2+l-1]),再在(O(n))的时间内把所以起点下标差为(len)的所以串对匹配完并且统计答案。
枚举(len)的复杂度乘以匹配的复杂度为(O(n^2)),我们成功消去了一个(l)!
但是这题还卡空间……所以(ans)数组开不了那么大。
可是出题人关上了一扇门,却为我们留了一扇窗。
(query)的次数很小。我们可以将([1,l])之内的所有数字都找到离自己最近的大于等于自己的(k),用一个(pos[i])记录这个(k)。然后每次匹配出来距离为(dis),我们就把答案累计到(ans[pos[dis]][i])里去,最后前缀和一下,就可以求出所有的(query)了。
时间复杂度:(O(n^2))
空间复杂度:(O(nq))
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e4+5;
bool v[maxn];
int n,m,l,cnt;
int ans[101][maxn];
int a[maxn],k[101],pos[maxn];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
void solve() {
for(int len=1;len<=n-l;len++) {//枚举len
int res=0,h1=1,h2=h1+len;//res记录当前两个串的距离,h1是第一个串的起点,h2是第二个串的起点
for(int i=0;i<l;i++)
if(a[h1+i]!=a[h2+i])res++;
ans[pos[res]][h1]++;
ans[pos[res]][h2]++;//答案累计到ans[pos[res]][h1]和ans[pos[res]][h2]里
while(1) {
if(h2+l>n)break;//如果h2往后没有l那么长了就break
res-=(a[h1]!=a[h2]);
res+=(a[h1+l]!=a[h2+l]);//O(1)转移
h1++;h2++;
ans[pos[res]][h1]++;
ans[pos[res]][h2]++;//累计答案
}
}
}
int main() {
n=read();l=read();
for(int i=1;i<=n;i++)
a[i]=read();
m=read();pos[1]=1;//pos[1]等于1
for(int i=1;i<=m;i++)
k[i]=read(),v[k[i]]=1;//我这里用差分的方法求每个数的pos
for(int i=2;i<=l;i++)
pos[i]=pos[i-1]+v[i-1];//如果是在(k[i],k[i+1]]之间的数,pos[i]就是i+1
solve();
for(int i=1;i<=n-l+1;i++)
for(int j=1;j<=m;j++)
ans[j][i]+=ans[j-1][i];//前缀和统计答案
for(int i=1;i<=m;i++) {
for(int j=1;j<=n-l+1;j++)
printf("%d ",ans[pos[k[i]]][j]);//离线输出答案
puts("");
}
return 0;
}