CF1080E Sonya and Matrix Beauty (字符串哈希,Manachar算法)

CF1080E Sonya and Matrix Beauty (字符串哈希,Manachar算法)

题目链接:CF1080E

这道题思维十分巧妙;

回文串有这样几个性质:
$1、$ 长度为偶数的回文串中所有字符出现次数均为偶数

$2、$ 长度为奇数的回文串中仅且只有一个字符出现次数为奇数,其余字符出现次数为偶数

所以我们只要统计一行中的每个字符出现次数便可判断该行是否成立

  • 对于每一行来说,考虑异或的性质,我们可以用异或前缀和和位运算,可以快速得出行的区间能否满足回文串的构造条件
  • 对于每一列来说,因为不能交换顺序,所以必须具有回文性质(关于中心点对称),这就需要要求每一行相同的字母数量应该相同

每一行我们通过压位+异或前缀和即可完成

根据第二个规律,因为只需要验证每一行是否相同,所以可以采用字符串 $hash$ 每一行,$O(N*M)$处理,$O(1)$验证

在考虑列的情况,我们发现只要 行的 $hash$ 值关于中心对称,就成立,我们对于 $hash$ 值跑Manacher

总时间复杂度 $O(M*M*N)$

 1 #include<bits/stdc++.h>
 2 #define MAXN 610
 3 using namespace std;
 4 typedef long long ll;
 5 typedef pair<ll,ll> par;
 6 const ll M1=19260817,M2=998244353;
 7 int n,m,ans;
 8 char a[MAXN][MAXN];
 9 int cnt[MAXN][MAXN][30],sum[MAXN][MAXN];
10 par H[MAXN][MAXN];
11 par Minus (const par &x,const par &y)
12 {
13     return make_pair ((x.first-y.first+M1)%M1,(x.second-y.second+M2)%M2);
14 }
15 par Hash (int *A)
16 {
17     par res;
18     res.first=res.second=0;
19     for (int i=0;i<26;i++)
20     {
21         res.first=(res.first+res.first*(m+1)+A[i])%M1;
22         res.second=(res.second+res.second*(m+1)+A[i])%M2;
23     }
24     return res;
25 }
26 bool check (int i,int l,int r)
27 {
28     int x=sum[i][r]^sum[i][l-1];
29     return x==0||(!(x-(x&-x)));
30 }
31 par s[MAXN<<1];
32 int p[MAXN<<1];
33 void manacher ()
34 {
35     int id=0,mx=0;
36     for (int i=1;i<=n*2;i++)
37         if (s[i].first>=0)
38         {
39             p[i]=mx>=i?min (p[id*2-i],mx-i):1;
40             while (i-p[i]>0&&s[i-p[i]]==s[i+p[i]]) p[i]++;
41             ans+=p[i]/2;
42             if (i+p[i]>mx)
43             {
44                 mx=i+p[i];
45                 id=i;
46             }
47         }
48 }
49 int main()
50 {
51     scanf ("%d%d",&n,&m);
52     for (int i=1;i<=n;i++)
53     {
54         scanf ("%s",a[i]+1);
55         for (int j=1;j<=m;j++)
56         {
57             for (int k=0;k<26;k++) cnt[i][j][k]+=cnt[i][j-1][k];
58             cnt[i][j][a[i][j]-'a']++;//求计数前缀和 
59             H[i][j]=Hash (cnt[i][j]);//字符串hash 
60             sum[i][j]=sum[i][j-1]^(1<<(a[i][j]-1));//异或前缀和 
61         }
62     }
63     for (int i=1;i<=m;i++)
64         for (int j=i;j<=m;j++)
65         {
66             for (int k=1;k<=n;k++)
67             {
68                 s[(k<<1)-1]=make_pair (0,0);
69                 if (check (k,i,j)) s[(k<<1)]=Minus (H[k][j],H[k][i-1]);
70                 else s[(k<<1)]=make_pair (-1ll*k,-11ll*k);
71             }
72             manacher ();
73         }
74     printf ("%d",ans);
75     return 0;
76 }
扫描二维码关注公众号,回复: 4390913 查看本文章

猜你喜欢

转载自www.cnblogs.com/PaulShi/p/10075178.html