首页 > 其他分享 >CF113B Petr# 题解

CF113B Petr# 题解

时间:2023-06-12 21:46:47浏览次数:45  
标签:int 题解 pnn long CF113B pa ull Petr 字符串

最近在做字符串的题,正好就给我随机了一道这个(

题意

给你一个字符串 \(s\) 以及一个开头串 \(s_{begin}\) 和结尾串 \(s_{end}\),问该字符串中有多少个不同的子串,满足以 \(s_{begin}\) 开头,以 \(s_{end}\) 结尾。两个子串不同,当且仅当两个子串长度不同,或长度相同但至少有一个位置上的字符不同,而与位置无关。

分析

首先,我们注意到这是一个找字符串中某一个模式串出现位置的题,考虑 KMP(事实上,这道题的数据范围甚至都可以暴力匹配位置);然后,发现我们只关注字符串的组成元素不同,考虑哈希。到这里了这道题实际上做完了。

然鹅,这道题个细节。比如给定字符串 abcdefghi,然后 \(s_{begin} =\) abcdef,$s_{end} = $ cd,这时候就需要注意,一定要保证你的子串是以 \(s_{end}\) 结尾的。

代码:

这里给出两种哈希思路(第二种是从题解中找到的qwq,太弱了导致我只会map暴力搞。)

第一种:用 unordered_map

因为如果直接用 map 会 T

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int N = 2050, pp = 33;//这里23会被卡,33就可以了。哈希真的玄学……

ull pnn[N];
char s[N], ls[N], rs[N];
int n, la, lb;
int nxt1[N], nxt2[N];
int pa[N], pb[N], tota, totb;
ull  hashb[N];

void init(){
    pnn[0] = 1;
    for(int i = 1; i<=n; ++i){
        hashb[i] = (hashb[i-1]*pp+s[i]);
        pnn[i] = pnn[i-1]*pp;
    }
}
unordered_map<ull, int> mb;
inline int ull gethashb(int l, int r){
    return (hashb[r]-hashb[l-1]*pnn[r-l+1]);
}
void KMP(){
    int j = 0;
    for(int i = 2; i<=la; i++){
        while(j && ls[j+1] != ls[i]) j = nxt1[j];
        if(ls[j+1] == ls[i]) j++;
        nxt1[i] = j;
    }
    j = 0;
    for(int i = 2; i<=lb; i++){
        while(j && rs[j+1] != rs[i]) j = nxt2[j];
        if(rs[j+1] == rs[i]) j++;
        nxt2[i] = j;
    }
    j = 0;
    for(int i = 1; i<=n; i++){
        while(j && ls[j+1] != s[i]) j = nxt1[j];
        if(ls[j+1] == s[i]) j++;
        if(j == la){
            pa[++tota] = i-la+1;
        }
    }
    j = 0;
    for(int i = 1; i<=n; i++){
        while(j && rs[j+1] != s[i]) j = nxt2[j];
        if(rs[j+1] == s[i]) j++;
        if(j == lb){
            pb[++totb] = i-lb+1;
        }
    }
    //前面就是匹配+找位置
    int tl, tr;
    int ans = 0;
    for(int i = 1; i<=tota; i++){
        for(int j = 1; j<=totb; j++){
            if(pb[j]>=pa[i]&&((pa[i]+la-1)<=(pb[j]+lb-1))){
                tl = pa[i], tr = pb[j]+lb-1;
                ull tmpb = gethashb(tl, tr);
                if(mb[tmpb]){
                    continue;
                } else{
                    mb[tmpb]++;
                    ans++;
                }
            }
        }
    }
    printf("%d\n", ans);
}
int main(){
    scanf("%s%s%s", s+1, ls+1, rs+1);
    n = strlen(s+1);
    la = strlen(ls+1);
    lb = strlen(rs+1);
    init();
    KMP();
    return 0;
}

第二种:用 unique 去重

这个也是最快的。

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
using namespace std;
const int N = 2050,  pp = 33;

ull pnn[N];
char s[N], ls[N], rs[N];
int n, la, lb;
int nxt1[N], nxt2[N];
int pa[N], pb[N], tota, totb;
ull hashb[N];
ull ans[N*N];
void init(){
    pnn[0] = 1;
    for(int i = 1; i<=n; ++i){
        hashb[i] = (hashb[i-1]*pp+s[i]);
        pnn[i] = pnn[i-1]*pp;
    }
}

inline int ull gethashb(int l, int r){
    return (hashb[r]-hashb[l-1]*pnn[r-l+1]);
}
void KMP(){
    int j = 0;
    for(int i = 2; i<=la; i++){
        while(j && ls[j+1] != ls[i]) j = nxt1[j];
        if(ls[j+1] == ls[i]) j++;
        nxt1[i] = j;
    }
    j = 0;
    for(int i = 2; i<=lb; i++){
        while(j && rs[j+1] != rs[i]) j = nxt2[j];
        if(rs[j+1] == rs[i]) j++;
        nxt2[i] = j;
    }
    j = 0;
    for(int i = 1; i<=n; i++){
        while(j && ls[j+1] != s[i]) j = nxt1[j];
        if(ls[j+1] == s[i]) j++;
        if(j == la){
            pa[++tota] = i-la+1;
        }
    }
    j = 0;
    for(int i = 1; i<=n; i++){
        while(j && rs[j+1] != s[i]) j = nxt2[j];
        if(rs[j+1] == s[i]) j++;
        if(j == lb){
            pb[++totb] = i-lb+1;
        }
    }
    int tl, tr;
    int tans = 0;
    for(int i = 1; i<=tota; i++){
        for(int j = 1; j<=totb; j++){
            if(pb[j]>=pa[i]&&((pa[i]+la-1)<=(pb[j]+lb-1))){
                tl = pa[i], tr = pb[j]+lb-1;
                ull tmpb = gethashb(tl, tr);
                ans[++tans] = tmpb;
            }
        }
    }
    sort(ans+1, ans+tans+1);
    tans = unique(ans+1, ans+tans+1)-ans-1;
    printf("%d\n", tans);
}
int main(){
    scanf("%s%s%s", s+1, ls+1, rs+1);
    n = strlen(s+1);
    la = strlen(ls+1);
    lb = strlen(rs+1);
    init();
    KMP();
    return 0;
}

标签:int,题解,pnn,long,CF113B,pa,ull,Petr,字符串
From: https://www.cnblogs.com/frostwood/p/17476167.html

相关文章

  • 【P8819 [CSP-S 2022]】 星战 题解(图论 + 哈希)
    图论+哈希。Link.因为实在是太妙了所以写个题解。Solution因为每个点的出度都为\(1\),所以从任意一点出发永远可以走下去,故每次只需判断每个点度数是否为\(1\)即可。然后一三操作显然直接\(O(1)\)维护,\(50\pts\)。考虑二四操作。每次操作显然对点\(u\)的出度......
  • [ABC212E] Safety Journey 题解
    SafetyJourney题目大意给定一张缺少了\(m\)条边的\(n\)个点的完全图和一个正整数\(k\),你需要求出满足以下条件的序列\(A\)的数量:\(A\)的长度为\(k+1\)。\(A_0=A_k=1\)。\(\forall0\lei\lek-1\),点\(A_i\)和点\(A_{i+1}\)之间存在边。思路分析图上计数,考......
  • Codeforces Round 877 (Div.2) 题解 A - D
    A.BlackboardList题目大意起初黑板上有两个数,现在不断选取两个数作出他们俩差的绝对值并写在黑板上,如此往复直到黑板上有\(n\)个数。现在给定这\(n\)个数,问起初两数的其中一个数是多少。解题思路我们分两种可能:要么这两个数有负数,要么没有。有负数的情况,因为每次写下......
  • P1707 刷题比赛 题解
    多少有点混乱邪恶。题意:给出递推式:\[a_1=b_1=c_1=1\\a_2=b_2=c_2=3\\\begin{aligned}a_k&=p\timesa_{k-1}+q\timesa_{k-2}&+b_{k-1}+c_{k-1}&+r(k-2)^2+t(k-2)+1\\b_k&=u\timesb_{k-1}+v\timesb_{k-2}&+a_{k-1}+c_{k-1}&+w^{k-2}\\c......
  • P1306 斐波那契公约数 题解
    请求出\(f_n\)与\(f_m\)的最大公约数,即\(\gcd(f_n,f_m)\),答案对\(10^8\)取模。结论:\(\gcd(f_n,f_m)=f_{\gcd(n,m)}\)证明如下:首先引理1:\[f_{n+m}=f_{n-1}\timesf_{m}+f_{n}\timesf_{m+1}\]运用归纳法,可以简单证明,此处略去。引理2:\[\gcd(f_n,f_......
  • AtCoder Beginner Contest 305 题解
    https://atcoder.jp/contests/abc305/tasks_printE-ArtGalleryonGraph冷知识:md这题赛时没做出来/cy刚看到题:这是什么题啊,\(K,h\)都\(1e5\)能做吗/fn确实能做。考虑类似SPFA的操作。设\(a_x\)表示\(x\)还可以对距离最多为\(a_x\)的点产生贡献,然后就直接......
  • Codeforces Round 876 Div2 A-D题解
    CodeforcesRound876Div2A-D题解A.TheGoodArray这个题就是问你对于\(i\leqn\),要求前面后面至少\(ceil(\frac{i}{k})\)个1那我们就贪心的每k个放一个1,或者直接用数学算一下就好了AC代码#include<bits/stdc++.h>usingnamespacestd;constexprintlimit=(......
  • Java8新特性Stream之list转map及问题解决
    List集合转Map,用到的是Stream中Collectors的toMap方法:Collectors.toMap具体用法实例如下://声明一个List集合Listlist=newArrayList();list.add(newPerson("1001","小A"));list.add(newPerson("1002","小B"));list.add(......
  • 杂题题解
    序列变化(exchange)只要第一位确定,后面的\(n-1\)位都是唯一确定的。因为具体是B还是C只取决于两侧字母是否一样,所以两种变化方案其中一种是另一种每一位取反,要么都合法,要么都不合法。但变化出的方案可能不能继续变化下去,比如CCC变化到BBB之后就不能再变化了,但变化到CCC......
  • permu题解(树上莫队)(非正解)
    题目传送门题意给出一个长度为$n$的排列$P(P1,P2,...Pn)$,以及$m$个询问。每次询问某个区间$[l,r]$中,最长的值域连续段长度。思路首先这道题事求连续段的长度,打个莫队先#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;namespaceTestify{ inlineint......