F. 地图压缩
样例
题意
给一个二维字符矩阵 , 每次询问给定一个区间 , 求这个区间的最小循环节。
思路
首先对于一维字符串的最小循环节(无需完全覆盖) , 方法就是求出kmp的nxt数组 , ans = len - nxt[len] 。
对于二维的情况 , 容易发现行与列之间是不冲突的 。
于是我们对行求一遍哈希 , 对列求一遍哈希 。
询问时把区间的每一行的哈希值取出来做一遍kmp的预处理 ,再把每一列的哈希值取出来做一遍kmp的预处理 , 两个结果相乘就是答案 。
这样处理的复杂度是 O(n*q) 的,满足题目要求 。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2020;
int a[2020];
int solve(int n){ // kmp的预处理
int pre[2020]= {0};
memset(pre,0,sizeof pre);
int j = 0;
for(int i=2;i<=n;i++){
while(j!=0&&a[i]!=a[j+1]) j = pre[j];
if(a[i]==a[j+1]) j++;
pre[i] = j;
}
return n - pre[n];
}
const int base = 27;
const int mod = 998244353;
int power[2020];
char mp[2020][2020];
int hash_r[2020][2020];
int hash_c[2020][2020];
signed main(){
ios::sync_with_stdio(false);
int n,q;
cin>>n>>q;
power[0] = 1;
for(int i=1;i<2020;i++){
power[i] = power[i-1]*base%mod;
}
for(int i=1;i<=n;i++){
cin>>(mp[i]+1);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
hash_r[i][j] = (hash_r[i][j-1]*base%mod+(mp[i][j]-'a'+1))%mod; // 行哈希
hash_c[i][j] = (hash_c[i-1][j]*base%mod+(mp[i][j]-'a'+1))%mod; // 列哈希
}
}
while(q--){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
int tot = 0;
memset(a,0,sizeof a);
// 求每一行的哈希值
for(int i=x1;i<=x2;i++){
a[++tot] = (hash_r[i][y2]-hash_r[i][y1-1]*power[y2-y1+1]%mod+mod)%mod;
}
int x = solve(tot);
memset(a,0,sizeof a);
tot = 0;
// 求每一列的哈希值
for(int i=y1;i<=y2;i++){
a[++tot] = (hash_c[x2][i] - hash_c[x1-1][i]*power[x2-x1+1]%mod+mod)%mod;
}
int y = solve(tot);
cout<<x*y<<endl;
}
}