• LOJ#2452. 「POI2010」反对称 Antisymmetry


    题目描述

    对于一个 (0/1) 字符串,如果将这个字符串 (0)(1) 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。比如 (00001111)(010101) 就是反对称的,而 (1001) 就不是。
    现在给出一个长度为 (n)(0/1) 字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算。

    输入格式

    第一行一个正整数 (n)
    第二行一个长度为 (n)(0/1) 字符串。

    输出格式

    一行一个整数,表示原串的反对称子串个数。

    样例

    样例输入

    8
    11001011
    

    样例输出

    7
    

    数据范围与提示

    对于 (100\%) 的数据, (1le nle 500 000)

    Translated by vincent163

    题解

    马拉车也是可以做的而且复杂度更优。
    我还是菜啊没有看出来单调性,这个取反翻转操作是单调的,你一个大的串如果是反对称串,中间的任何一个也肯定是。因为翻转的位置并没有变...
    那么枚举起点,二分子串长度判断即可。
    因为取反后翻转这个操作,所以不可能有奇数串符合条件(奇数串取反后翻转了一定不相等),所以判断也很好写
    然后比较坑的就是答案要用longlong存,我因为这个爆了好几发,一开始还以为是进制数选的不好。
    复杂度(O(nlogn))

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    typedef long long ll;
    const int N = 500010;
    const ull base = 13131;
    
    ull h1[N], h2[N], p[N];
    int n;
    ll ans = 0;
    char s[N];
    
    ull get_h1(int l, int r) { return h1[r] - h1[l - 1] * p[r - l + 1]; }
    ull get_h2(int l, int r) { return h2[l] - h2[r + 1] * p[r - l + 1]; }
    
    int check(int x) {
    	int l = 1, r = min(x, n - x);
    	while(l <= r) {
    		int mid = (l + r) >> 1;
    		if(get_h1(x - mid + 1, x) == get_h2(x + 1, x + mid)) l = mid + 1;
    		else r = mid - 1;
    	}
    	return r;
    }
    
    int main() {
    	scanf("%d%s", &n, s + 1); p[0] = 1;
    	for(int i = 1; i <= n; ++i) p[i] = p[i - 1] * base, h1[i] = h1[i - 1] * base + (ull)s[i];
    	for(int i = n; i; --i) h2[i] = h2[i + 1] * base + (ull)(s[i] == '0' ? s[i] + 1 : s[i] - 1);
    	for(int i = 1; i < n; ++i) ans += check(i);
    	printf("%lld
    ", ans);
    }
    
  • 相关阅读:
    DDL-表的管理
    DDL-库的管理
    Linux虚拟机--进入MySQL报错的解决办法
    前端底层-原型
    前端底层-对象与构造函数
    前端底层-this
    前端底层-DOM
    前端底层-数据类型与数据的三大存储格式
    前端底层-函数
    前端底层-数据类型与全局属性
  • 原文地址:https://www.cnblogs.com/henry-1202/p/10321013.html
Copyright © 2020-2023  润新知