牛客国庆集训派对Day5 H题 我不爱她

2023-10-24 14:10
文章标签 牛客 国庆 day5 集训 派对

本文主要是介绍牛客国庆集训派对Day5 H题 我不爱她,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

Catalog

文章目录

      • Catalog
        • Problem:传送门
      • Solution:
        • AC_Code:
        • Problem Description:

Problem:传送门

 Portal

 原题目描述在最下面。


Solution:

先放一个群里某大佬的解释:

就是一个串w是a的前缀也是b的后缀
那么border(w)也是a的前缀也是b的后缀
(因为border(w)是w的前缀也是w的后缀)
所以把每个w的贡献记为len(w) - len(border(w))
如果w被匹配了 说明border(w)一定不是最长的 相当于加上len(w)的时候 把不是最长的len(border(w))贡献去掉
最后累加起来剩下的都是最长的


在告诉你一个小秘密:

int ans = 0, t = 0;
for(int i = len; i > 0; ) {ans += i - nex[i];i = nex[i];p[t++] = nex[i];
}
printf("ans = %d\n", ans);///ans 的值和这个字符串的长度一样哦!!!
///只有p数组里存的前缀才是这个字符串的后缀!(如果这句话是错的,望大佬指出!

 讲讲我的理解吧

 如那位大佬所言,若字符串 w w w是所求的巧合值,也就是 w w w a a a的后缀,也是 b b b的后缀,同样 b o r d e r ( w ) border(w) border(w)也满足此条件,只不过 l e n ( b o r d e r ( w ) ) &lt; l e n ( w ) len(border(w))&lt;len(w) len(border(w))<len(w)

 问题在于,你对每两个字符串求 b o r d e r border border时间上肯定 t l e tle tle了。怎么解决呢?

 题解上讲的是每个前缀/后缀的贡献记为 l e n ( w ) − l e n ( b o r d e r ( w ) ) len(w)-len(border(w)) len(w)len(border(w))

 然后你把所有的前缀子串贡献记为 l e n ( i ) − l e n ( b o r d e r ( i ) ) len(i)-len(border(i)) len(i)len(border(i)),再用 m a p map map存一下每个前缀 o r or or后缀的出现次数。最后遍历一遍所有后缀或前缀,累加贡献即可。

 我上面写了只有 p p p数组里的前缀才会是这个字符串的后缀!而且每个字符串不会有算重复的地方, p p p数组求一遍 k m p kmp kmp f a i l fail fail数组就行了。


AC_Code:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <unordered_map>
#define fi first
#define se second
#define iis std::ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, LL> pii;
/*
就是一个串w是a的前缀也是b的后缀
那么border(w)也是a的前缀也是b的后缀
(因为border(w)是w的前缀也是w的后缀)
所以把每个w的贡献记为len(w) - len(border(w))
如果w被匹配了 说明border(w)一定不是最长的 相当于加上len(w)的时候 把不是最长的len(border(w))贡献去掉
最后累加起来剩下的都是最长的
int ans = 0, t = 0;
for(int i = len; i > 0; ) {ans += i - nex[i];i = nex[i];p[t++] = nex[i];
}
printf("ans = %d\n", ans);///ans 的值和这个字符串的长度一样哦!!!
///只有p数组里存的前缀才是这个字符串的后缀!(如果这句话是错的,望大佬指出!
*/
const long long HMOD[] = {2078526727, 2117566807};
const long long BASE[] = {1572872831, 1971536491};
const int MXN = 1e5 + 5;
const int MXT = 1e6 + 5e5 + 6;char s[MXT];
int fail[MXT];
std::vector<pii> ve;
unordered_map<LL, int> mp;
void get_fail(char *t,int m) {fail[0] = -1;for(int i = 0,k = -1; i < m;) {if(k == -1 || t[i] == t[k]) {++ i; ++ k;fail[i] = k;}else k = fail[k];}
}
int main(int argc, char const *argv[]) {LL N = (int)2e9, hh;int tim;scanf("%d", &tim);while(tim--) {ve.clear();mp.clear();int n; scanf("%d", &n);for(int i = 0; i < n; ++i) {scanf("%s", s);int len = strlen(s);get_fail(s, len);pii tmp = {0, 0};for(int i = 0; i < len; ++i) {tmp.fi = (tmp.fi*BASE[0] + (s[i]-'a'+1))%HMOD[0];tmp.se = (tmp.se*BASE[1] + (s[i]-'a'+1))%HMOD[1];hh = tmp.fi*N + tmp.se;ve.push_back({hh, i + 1 - fail[i + 1]});}tmp = {0, 0};LL p1 = 1, p2 = 1;for(int i = len - 1; i >= 0; --i) {tmp.fi = (tmp.fi + (s[i]-'a'+1)*p1)%HMOD[0];tmp.se = (tmp.se + (s[i]-'a'+1)*p2)%HMOD[1];hh = tmp.fi*N + tmp.se;mp[hh] ++;p1 = p1 * BASE[0] % HMOD[0];p2 = p2 * BASE[1] % HMOD[1];}}int len = ve.size();LL ans = 0;for(int i = 0; i < len; ++i) {ans += mp[ve[i].first]*ve[i].se;}printf("%lld\n", ans);}return 0;
}
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <unordered_map>
#define fi first
#define se second
#define iis std::ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, LL> pii;const int MXN = 1e5 + 5;
const int MXT = 1e6 + 5e5 + 6;
const LL mod = 999999999999989ll;
const LL base = 99957;
int n;
vector<int> g[MXN];
char s[MXT];
int ch[MXT][2], CNT;
int fail[MXT];
LL hs[MXT];
unordered_map<LL, LL> mp;
int newnode() {++ CNT;ch[CNT][0] = ch[CNT][1] = -1;return CNT;
}
void Insert(vector<int> vs, int len) {fail[0] = -1;int rt = 0;for(int i = 0, k = -1; i < len; ) {if(k == -1 || vs[i] == vs[k]) {++ i; ++ k;fail[i] = k;if(ch[rt][vs[i-1]] == -1) {ch[rt][vs[i-1]] = newnode();}hs[ch[rt][vs[i-1]]] = (hs[rt] * base + vs[i-1] + 'a') % mod;rt = ch[rt][vs[i-1]];mp[hs[rt]] += i - fail[i];printf("%d %lld\n", i - fail[i], hs[rt]);}else k = fail[k];}
}
int main(){int T;scanf("%d",&T);while(T--){mp.clear();CNT = 0;ch[0][0] = ch[0][1] = -1;scanf("%d", &n);for(int j = 0, len; j < n; ++j) {scanf("%s", s);len = strlen(s);for(int i = 0; i < len; ++i) {g[j].push_back(s[i]-'a');}Insert(g[j], len);}LL ans=0;for(int i = 0, len; i < n; ++i) {len = g[i].size();LL p1 = 1, tmp = 0;for(int j = len - 1; j >= 0; --j) {tmp = (tmp + (g[i][j] + 'a') * p1) % mod;p1 = p1 * base % mod;ans += mp[tmp];}g[i].clear();}printf("%lld\n",ans);}
}

Problem Description:

终于活成了自己讨厌的样子。

天空仍灿烂,它爱着大海。

你喜欢大海,我爱过你。

世界上充满了巧合。我们把每句话当成一个字符串,我们定义a对b的巧合值为a的最长后缀的长度并且它是恰好是b的前缀,这里的后缀或者前缀包括字符串的本身。
比如字符串“天空仍灿烂她喜欢大海”对“她喜欢大海我不爱她了我爱的只是与她初见时蔚蓝的天空”的巧合值为5,而字符串“她喜欢大海我不爱她了我爱的只是与她初见时蔚蓝的天空”对“天空仍灿烂她喜欢大海”的巧合值为2。
现在给出n个字符串由"ab"构成的字符串s1,s2,…,sn,求出对于所有1≤ i,j≤ n,si对sj的巧合值的和。

e

这篇关于牛客国庆集训派对Day5 H题 我不爱她的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/275720

相关文章

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

牛客小白月赛100(A,B,C,D,E,F三元环计数)

比赛链接 官方讲解 这场比较简单,ABC都很签到,D是个不太裸需要预处理的 B F S BFS BFS 搜索,E是调和级数暴力枚举,F是三元环计数。三元环考的比较少,没见过可能会偏难。 A ACM中的A题 思路: 就是枚举每个边变成原来的两倍,然后看看两短边之和是否大于第三边即可。 不能只给最短边乘 2 2 2,比如 1 4 8 这组数据,也不能只给第二短边乘 2 2 2,比

笔试强训,[NOIP2002普及组]过河卒牛客.游游的水果大礼包牛客.买卖股票的最好时机(二)二叉树非递归前序遍历

目录 [NOIP2002普及组]过河卒 牛客.游游的水果大礼包 牛客.买卖股票的最好时机(二) 二叉树非递归前序遍历 [NOIP2002普及组]过河卒 题里面给的提示很有用,那个马的关系,后面就注意,dp需要作为long的类型。 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息publ

2014暑假集训搜索专题

A - 漫步校园 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Description LL最近沉迷于AC不能自拔,每天寝室、机房两点一线。由于长时间坐在电脑边,缺乏运动。他决定充分利用每次从寝室到机房的时间,在校园里散散步。整个HDU校园呈方形布局,可划

每日OJ_牛客_求和(递归深搜)

目录 牛客_求和(递归深搜) 解析代码 牛客_求和(递归深搜) 求和_好未来笔试题_牛客网 解析代码         递归中每次累加一个新的数,如果累加和大于等于目标,结束递归。此时如果累加和正好等于目标,则打印组合。向上回退搜索其它组合。此题本身就是一个搜索的过程,找到所有的组合。 #include <iostream>#include <cmath>#in

中秋国庆请客喝酒,面子与钱包双赢的红酒选择

平时生活中,总少不了各种聚会,不管是朋友小聚,还是正式的商务宴请,酒都是少不了的,而现在,越来越多的人都喜欢选择红酒来助兴。 喝红酒的人不少,懂红酒的人却不多。有时候真的很尴尬,明明环境菜都不错,就是红酒太难喝,每一口都要鼓足勇气才能下咽。 其实,酒也是饭局的重要组成部分,如果酒不好喝,客人事后也是会暗暗吐槽的。所以,一个好的饭局,酒一定也是好的。 这里说的“好”,既要面子上

牛客《剑指Offer》 -- 数值的整数次方

题目描述 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。 思路 特别注意负数的情况,出现负数,将其转化为正数然后求倒数。 class Solution {public:double Power(double base, int exponent) {double total = 1;bool flag = false

牛客网《剑指Offer》 二进制中1的个数

题目描述 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 思路 负数用补码,其实就是求一个数据在计算机中是存储是怎么样子的。用位运算,就能很好实现。 class Solution {public:int NumberOf1(int n) {int count = 0;int flag = 1;while (flag != 0) {if ((n & f

牛客网《剑指Offer》 矩形覆盖

题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? class Solution {public:int rectCover(int number) {if(number==0) return 0;if(number==1) return 1;if(number==2) return 2;retu