zoukankan      html  css  js  c++  java
  • @codefoces


    @description@

    给定两个长度为 n 的字符串 a, b 与一个长度为 m 的字符串 s。

    问存在多少对区间 [l1, r1], [l2, r2](1 <= l1 <= r1 <= n, 1 <= l2 <= r2 <= n),使得:

    1)两个区间含有交集。即存在 x 满足 l1 <= x <= r1 且 l2 <= x <= r2。
    2)a[l1...r1] + b[l2...r2] = s。

    原题传送门。

    @solution@

    区间要有交集,可以等价认为是 l1 <= r2 且 l2 <= r1。

    由题,区间长度和 (r1 - l1 + 1) + (r2 - l2 + 1) = m。
    变形一下得到 (r2 - l1 + 1) + (r1 - l2 + 1) = m。
    结合上面要有交集的不等式,可以得到 1 <= (r2 - l1 + 1) < m。

    我们使用 z-algorithm 求出 a 中每一个 l1 与 s 的最长公共前缀,求出 b 中每一个 r2 与 s 的最长公共后缀。

    接着,从后往前扫描 a 中的每一个 l1,维护 x[i] 表示如果 r1 - l1 + 1 = i 时对应了多少 r2。查询只需要区间求和即可。
    因为 1 <= (r2 - l1 + 1) < m,得到 r2 的取值范围实际上是个滑动的区间。每一个 r2 的贡献是一个区间加。

    因此直接用树状数组即可。

    @accepted code@

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 500000;
    const int MAXM = 2*MAXN;
    
    int z[MAXM + MAXN + 5];
    char str[MAXM + MAXN + 5];
    
    void getZ(int len) {
    	int pos = 0, mxl = -1;
    	for(int i=1;i<len;i++) {
    		z[i] = (mxl >= i ? min(mxl - i + 1, z[i - pos]) : 0);
    		while( str[z[i]] == str[i+z[i]] ) z[i]++;
    		if( mxl < i + z[i] - 1 ) pos = i, mxl = i + z[i] - 1;
    	}
    }
    void get(char *S, int lenS, char *T, int lenT, int *f) {
    	for(int i=0;i<lenS;i++) str[i] = S[i];
    	str[lenS] = 1;
    	for(int i=0;i<lenT;i++) str[lenS+1+i] = T[i];
    	getZ(lenS + lenT + 1);
    	for(int i=0;i<lenT;i++) f[i] = z[lenS+1+i];
    }
    
    int n, m; ll T[2][MAXM + 5];
    int lowbit(int x) {return (x & -x);}
    void add(int x, ll d, int t) {
    	for(int i=x;i<=m;i+=lowbit(i))
    		T[t][i] += d;
    }
    void modify(int l, int r, ll d) {
    	add(l, d, 0), add(l, d*l, 1);
    	add(r + 1, -d, 0), add(r + 1, -d*(r + 1), 1);
    }
    ll sum(int x, int t) {
    	ll ret = 0;
    	for(int i=x;i;i-=lowbit(i))
    		ret += T[t][i];
    	return ret;
    }
    ll query(int l, int r) {
    	ll A = l*sum(l - 1, 0) - sum(l - 1, 1);
    	ll B = (r + 1)*sum(r, 0) - sum(r, 1);
    	return B - A;
    }
    
    char a[MAXN + 5], b[MAXN + 5], s[MAXM + 5];
    int af[MAXN + 5], bf[MAXN + 5];
    int main() {
    	scanf("%d%d%s%s%s", &n, &m, a, b, s);
    	get(s, m, a, n, af);
    	reverse(b, b + n), reverse(s, s + m);
    	get(s, m, b, n, bf);
    	reverse(bf, bf + n);
    	ll ans = 0;
    	for(int i=n-1;i>=0;i--) {
    		modify(max(m - bf[i], 1), m - 1, 1);
    		if( i + m - 1 < n )
    			modify(max(m - bf[i + m - 1], 1), m - 1, -1);
    		ans += query(1, af[i]);
    	}
    	printf("%lld
    ", ans);
    }
    

    @details@

    小清新的题目。

    差点忘了树状数组的区间加与区间求和怎么写了。。。

  • 相关阅读:
    day22 Pythonpython 本文json模块
    day22 Pythonpython 本文sys模块
    day22 Pythonpython random随机模块:略!!!本文os模块
    进程控制
    进程与内存、进程状态
    从汇编角度来理解递归工作栈的原理
    顺序栈
    迷宫问题 Maze 4X4 (sloved by backtrack)
    GDB调试
    用于文件系统的C库函数
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12407978.html
Copyright © 2011-2022 走看看