• P1117


    P1117

    跟这道题很像:CF319D(这也是黑色的)

    分析

    这是一道后缀SA+ST的题目,但是众所周知字符串哈希是一种异常优秀的算法,所以我们使用字符串哈希解决这一道问题。在这道题中字符串哈希相比于SA的方法虽然慢了一个(O(logn)),但是依旧在复杂度可承受范围内。

    在这道题中我们需要求出拆分为AABB形态的方案数量。而我们发现AABB形态就是由两个AA状态的字符串连接形成。

    如果我们设(A_i)表示以i位结尾的AA串数量,以(B_i)表示以i位开头的AA串数量。则很显然若我们将第i位及以前的部分放一个AA串,在第i+1及以后放一个AA串,他们就可以连在一起成为一个AABB串,所以当前状态对答案的贡献即为(A_i * B_i)

    如果我们可以求解出(A)(B),则答案即为:

    [sumlimits_{i=1}^{n-1}A_i*B_{i+1} ]

    现在的问题就是如何求解出A数组与B数组。

    如果我们在字符串上每隔(len)设置一个观察点,相邻观察点的最长公共前缀(LCP)与最长公共后缀(LCS)加起来构成的字符串即为经过两点上的最长相同串,并且若(LCP+LCS>=len),说明它们相交。若相交有重叠部分,则我们可以通过平移得到多个可行的摆放方案,可以缩短两个区间的尾部,也可以缩短两个区间的头部以使他们不重合(详见代码)。

    至于如何求LCP和LCS,当然可以用SA,不过我们这里还是采用字符串哈希,毕竟它写起来十分简易。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mod = 1e9 + 7;
    const int maxn = 30005;
    
    int a[maxn], b[maxn];
    long long w[maxn], p[maxn];
    char s[maxn];
    
    inline int get(int l, int r)
    { //计算字符串中l~r段的哈希值
    	return ((w[r] - w[l - 1] * p[r - l + 1]) % mod + mod) % mod;
    }
    
    int n, now;
    
    int lcp(int x, int y) //求最长前缀
    {
    	int l = 0, r = now;
    	while (l < r)
    	{
    		int mid = l + r + 1 >> 1;
    		if (get(x - mid + 1, x) == get(y - mid + 1, y))
    			l = mid;
    		else
    			r = mid - 1;
    	}
    	return l;
    }
    
    int lcs(int x, int y) //求最长后缀
    {
    	int l = 0, r = n - y + 1;
    	while (l < r)
    	{
    		int mid = l + r + 1 >> 1;
    		if (get(x, x + mid - 1) == get(y, y + mid - 1))
    			l = mid;
    		else
    			r = mid - 1;
    	}
    	return l;
    }
    
    int main()
    {
    	int T;
    	scanf("%d", &T);
    	while (T--)
    	{
    		memset(a, 0, sizeof(a));
    		memset(b, 0, sizeof(b));
    		scanf("%s", s + 1);
    		n = strlen(s + 1);
    		p[0] = 1;
    		for (int i = 1; i <= n; i++)
    		{ //字符串哈希
    			p[i] = p[i - 1] * 31 % mod;
    			w[i] = (w[i - 1] * 31 + s[i] - 'a') % mod;
    		}
    		for (int len = 1; len <= n; len++)
    		{							   //从小到大枚举删除区间的长度
    			int j = len, k = len << 1; //两个观察点
    			now = len;
    			while (k <= n)
    			{
    				int LCP = lcp(j, k), LCS = lcs(j, k);
    				int hd = max(k - LCP + len, k), tl = min(k + LCS - 1, k + len - 1); //两个块的尾位置
    				if (hd <= tl)
    				{
    					a[hd]++, a[tl + 1]--;						  //两个尾部之间的空间
    					b[hd - len * 2 + 1]++, b[tl - len * 2 + 2]--; //两个头部之间的空间
    				}
    				j += len, k += len; //转移到下一个观察点
    			}
    		}
    		for (int i = 1; i <= n; i++)
    			a[i] += a[i - 1], b[i] += b[i - 1];
    		long long answer = 0;
    		for (int i = 1; i < n; i++)
    			answer += a[i] * b[i + 1];
    		printf("%lld
    ", answer);
    	}
    	return 0;
    }
    
  • 相关阅读:
    GPU上的随机森林:比Apache Spark快2000倍
    基于TensorFlow2.x的实时多人二维姿势估计
    用code2vec、glow和spaCy进行词嵌入
    如何进行数据科学竞赛?(EDA)
    Python性能分析技巧
    PyTorch中基于TPU的FastAI多类图像分类
    GitHub上的AutoML
    Keras vs PyTorch vs Caffe:CNN实现对比
    深度学习的学习率调节实践
    win7下MariaDB10.0的my.ini配置文件的位置
  • 原文地址:https://www.cnblogs.com/macesuted/p/solution-P1117.html
Copyright © 2020-2023  润新知