zoukankan      html  css  js  c++  java
  • 【Good Bye 2020 G】Song of the Sirens

    题目链接

    链接

    翻译

    给你一个字符串 (s_i) 的生成规则,(s_{i+1}=s_it_is_i)

    因为 (t) 的长度为 (n), 所以一直会生成到第 (n+1) 个字符串。

    然后给你 (Q) 个询问,第 (i) 个询问会问你字符串 (w)(s_k) 中出现的次数。

    题解

    KMP,思维,前缀和。

    大概的做法就是,(w) 的出现次数是前一首歌的两倍,然后加上中间新增的那个字符可能的贡献次数,然后递推一下

    找到第一个大于等于 (w) 的歌曲 (idx), 对于 (w)(s[idx]) 中的次数,直接 (KMP), 后续的 (g(i,w)) ((g(i,w))(w)(s_i) 中包含了 (t_i) 这个字符的匹配数)可以发现就是 (w)

    (s[idx]) 的前端和末尾的匹配,也用 (KMP) 做就行。

    照着官方题解写的。

    重定向一下连接 我是链接

    在代码中一些比较难懂的地方加了注释, 具体看代码中的注释吧。

    代码

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    
    const int MOD = 1e9 + 7;
    const int N = 1e5;
    const int MAXW = 1e6;
    
    int n, q;
    int _pow[N + 10];
    //sum[i][j] 表示的是t的前 i 个字符中 j出现的次数,以及每个出现位置乘上对应的2的幂次权重之后的结果。
    LL sum[N + 10][26 + 5];
    vector<string> vS;
    string s0, t;
    
    /*
    	先算2^p
    */
    void pre() {
    	_pow[0] = 1;
    	for (int i = 1; i <= N; i++) {
    		_pow[i] = _pow[i - 1] * 2 % MOD;
    	}
    	//得到长度不超过10^6的所有的s
    	vS.push_back(s0);
    	int len = s0.size();
    	int idx = 0;
    	while (idx < n && len < MAXW) {
    		string si = vS.back();
    		string sipo = si + t[idx] + si;
    		vS.push_back(sipo);
    		idx++;
    		len = len * 2 + 1;
    	}
    	for (char key = 'a'; key <= 'z'; key++) {
    		int j = key - 'a';
    		for (int i = 0; i < n; i++) {
    			sum[i + 1][j] = 2*sum[i][j] + (key == t[i]);
    			sum[i + 1][j] %= MOD;
    		}
    	}
    }
    
    //f 会返回最长的前后缀长度。
    vector<int> doKMP(string s) {
    	vector<int> f((int)s.size(), 0);
    	int len = s.size();
    	int j = 0;
    	for (int i = 1; i < len; i++) {
    		while (j > 0 && s[i] != s[j]) {
    			j = f[j - 1];
    		}
    		if (s[i] == s[j]) {
    			j++;
    		}
    		f[i] = j;
    	}
    	return f;
    }
    
    //返回 w 和 s[idx] 前/尾  尾/前的匹配情况。
    vector<bool> getMatch(vector<int> f,int lenw) {
    	vector<bool> can(lenw + 1, false);
    	int cur = f.back();
    	while (cur > 0) {
    		can[cur] = true;
    		cur = f[cur - 1];
    	}
    	//匹配 0 个的话,直接算成功。
    	can[cur] = true;
    	return can;
    }
    
    int answer(int k, string w) {
    	//先找到第一个长度大于等于 w 的s[k]
    	int idx = 0;
    	int lenw = w.size();
    	while ((int)vS[idx].size() < lenw) {
    		idx++;
    	}
    	//vS[idx].size() >= lenw
    	//如果s[k]的长度小于 w,那么直接拉闸。
    	if (idx > k) {
    		return 0;
    	}
    	//s[idx]是第一个大于等于w的
    	//首先计算w在 s[idx]中出现的次数,这个用KMP搞。
    	//为了同时便于做w开头一部分和s[idx]尾部的匹配,以及w尾部的一部分和s[idx]开头的匹配
    	//因为后面的s[idx..k]都是 类似 a,s[idx],t[x],s[idx],b 这样的形式,所以 包含中间 t[x]的话
    	//一定是 s[idx]的尾部以及 s[idx]的头部分别和 w的头部、尾部匹配。
    	//因此,我们把 w 放在 s[idx] 的开头,做一下KMP,以及把 W 放在 s[idx] 的末尾再做一下KMP。
    	string tmp = w + "#" + vS[idx];
    	vector<int> fPre = doKMP(tmp);
    	tmp = vS[idx] + "#" + w;
    	vector<int> fSuf = doKMP(tmp);
    
    	//获取w的前部在s[idx]的尾部的匹配情况
    	vector<bool> mPre = getMatch(fPre,lenw);
    	//获取w的尾部在s[idx]的前部匹配情况。
    	vector<bool> mSuf = getMatch(fSuf,lenw);
    
    	//先计算前面的 f(idx,w)*2^{n-idx}
    	LL temp = 0;
    	//计算w在s[idx]中的匹配数
    	for (int x : fPre) {
    		if (x == lenw) {
    			temp++;
    		}
    	}
    	temp = temp * _pow[k - idx] % MOD;
    
    	//计算∑_{i=idx+1}^{k}f(i,w)*2^{n-i}
    	for (int i = 1; i <= lenw; i++) {
    		if (!mPre[i - 1] || !mSuf[lenw - i]) {
    			continue;
    		}
    		int j = w[i - 1] - 'a';
    		//s[idx+1..k] 这些歌曲,中间有多少个 j, 就能匹配多少次,所以要加上这一段的 sum
    		temp = temp + sum[k][j] - sum[idx][j] * _pow[k - idx]%MOD;
    		temp %= MOD;
    		temp = (temp + MOD) % MOD;
    	}
    	return temp;
    }
    
    int main() {
    	#ifdef LOCAL_DEFINE
    		freopen("in.txt", "r", stdin);
    	#endif // LOCAL_DEFINE
    
    	//输入n,q,s0,t
    	cin >> n >> q;
    	cin >> s0 >> t;
    	pre();
    	while (q--) {
    		int k;
    		string w;
    		cin >> k >> w;
    		cout << answer(k, w) << endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    node.js 的简单介绍
    vue浅析
    rest_framework的分页器组件配置与使用
    restframwork组件的权限认证
    关于and和or的运算
    restframwork组件的使用
    实现简单的子页面传值给父页面
    Django使用orm模块时想看多对对数据关系的配置
    Django更新数据库表时无法执行表修改 指定Django要使用的数据库
    图论-kruskal算法-稀疏图
  • 原文地址:https://www.cnblogs.com/AWCXV/p/14425873.html
Copyright © 2011-2022 走看看