Description
Zxl有一次决定制造一条项链,她以非常便宜的价格买了一长条鲜艳的珊瑚珠子,她现在也有一个机器,能把这条珠子切成很多块(子串),每块有k(k>0)个珠子,如果这条珠子的长度不是k的倍数,最后一块小于k的就不要拉(nc真浪费),保证珠子的长度为正整数。 Zxl喜欢多样的项链,为她应该怎样选择数字k来尽可能得到更多的不同的子串感到好奇,子串都是可以反转的,换句话说,子串(1,2,3)和(3,2,1)是一样的。写一个程序,为Zxl决定最适合的k从而获得最多不同的子串。 例如:这一串珠子是: (1,1,1,2,2,2,3,3,3,1,2,3,3,1,2,2,1,3,3,2,1), k=1的时候,我们得到3个不同的子串: (1),(2),(3) k=2的时候,我们得到6个不同的子串: (1,1),(1,2),(2,2),(3,3),(3,1),(2,3) k=3的时候,我们得到5个不同的子串: (1,1,1),(2,2,2),(3,3,3),(1,2,3),(3,1,2) k=4的时候,我们得到5个不同的子串: (1,1,1,2),(2,2,3,3),(3,1,2,3),(3,1,2,2),(1,3,3,2)
Input
共有两行,第一行一个整数n代表珠子的长度,(n<=200000),第二行是由空格分开的颜色ai(1<=ai<=n)
Output
也有两行,第一行两个整数,第一个整数代表能获得的最大不同的子串个数,第二个整数代表能获得最大值的k的个数,第二行输出所有的k(中间有空格)。
Sample Input
21
1 1 1 2 2 2 3 3 3 1 2 3 3 1 2 2 1 3 3 2 1
Sample Output
6 1
2
解题思路
这个题是一个字符串各不同长度子串中不重复的个数的问题,
可以使用字符串hash,暴力枚举算出每种长度子串的hash,找出不重复的个数
特殊之处,项链可以反转,因此(1,2,3)和(3,2,1)认为是一样的,因此需要计算每个子串正序的hash和反序的hash,并把这两个hash值乘起来作为最终的hash值,这样(1,2,3)和(3,2,1)就认为是一样的了
借鉴冬令营清华尹昊萱的代码进行分析
需要的数据结构
两个存放正序hash和反序hash的数据:
#define LL unsigned long long
const int maxn=200010;
LL h[maxn],g[maxn] //h是正序的,g是反序的
计算字符串hash时,依据的公式是:
hash[i] = ( hash[i−1]) ∗ p + id(s[i] ) % mod
这里要把字符串当初一个P进制数处理,p和mod应为质数,且p<mod
一般p可取13331。
对于mod 可以将结果存放在一个unsigned long long中
另外,还需要一个数组pow[ ]存放P进制数每一位的权值来提高效率
const int maxn=200010,p=13331;
LL pow[maxn];
每一个子串的hash值存入一个set中:
#include<set>
set<LL> s;
代码分析
首先读入整个字符串,放在数组a[]中,同时通过两个n次循环,计算正序和反序的每一个hash值:
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]); //读入整个字符串
for (int i=1;i<=n;i++) h[i]=h[i-1]*p+a[i]; //计算每一个正序的hash值,下标从1开始
for (int i=1;i<=n;i++) g[i]=g[i-1]*p+a[n-i+1]; //计算每一个反序的hash值,下标从1开始
准备好P进制的每一位权值,放在pow数组中:
pow[0]=1;
for (int i=1;i<=n;i++) pow[i]=pow[i-1]*p;
j接下来,要做一个两重循环:
外层循环i,实际上是控制子串的长度,即每次切割项链的长度,第一次是1,每次切长度=1的子串,第二次i=2,每次切长度=2的子串,…最后一次i=n,切长度为n的子串,就是整个字符串。
内层循环j,控制每次切的子串的区间,包括正序的和逆序的。当i=1时,每次切长度为1个字符的子串,j=1时,正序切的是s1,反序切的是sn;j=2时,正序切的s2,反序切的s(n-1)…
当i=2时,每次切长度为2个字符的子串,j=1时,切的是s1s2,sns(n-1);j=3时,切的是s2s3,s(n-2)s(n-3)。。。
对于不同的子串,通过以下公式计算其hash值:
hash[sl…sr] = hash[r] - hash[l-1] * p^( r-l+1)^
外层循环i | 子串长度 | 内层循环j | 正序子串 | 正序hash值 | 反序子串 | 反序hash值 |
---|---|---|---|---|---|---|
i=1 | 1 | j=1 | s1 | x[1…1]=h[1]-h[0]*pow[1] | s1 | g[n…n]=g[n]-g[n-1]pow[1] |
i=1 | 1 | j=2 | s2 | x[2…2]=h[2]-h[1]*pow[1] | s2 | g[n-1…n-1]=g[n-1]-g[n-2]pow[1] |
i=1 | 1 | j=3 | s3 | x[3…3]=h[3]-h[2]*pow[1] | s3 | g[n-2…n-2]=g[n-2]-g[n-3]pow[1] |
… | … | … | … | … | … | … |
i=2 | 2 | j=1 | s1s2 | x[1…2]=h[2]-h[0]*pow[2] | s2s1 | g[n…n-1]=g[n]-g[n-2]pow[2] |
i=2 | 2 | j=3 | s3s4 | x[3…4]=h[4]-h[2]*pow[2] | s4s3 | g[n-2…n-3]=g[n-2]-g[n-4]pow[2] |
… | … | … | … | … | … | … |
i=3 | 3 | j=1 | s1s2s3 | x[1…3]=h[3]-h[0]*pow[3] | s3s2s1 | g[n…n-2]=g[n]-g[n-3]pow[3] |
i=3 | 3 | j=4 | s4s5s6 | x[4…6]=h[6]-h[3]*pow[3] | s6s5s4 | g[n-3…n-5]=g[n-3]-g[n-6]pow[3] |
… | … | … | … | … | … | … |