2020牛客多校第二场A题 All with Pairs Hash+KMP

本文详细解析了AllwithPairs算法,介绍了如何通过哈希方法统计字符串的所有后缀出现次数,并运用容斥原理解决重复计算问题,最终计算出所有字符串对的最大匹配平方和。

All with Pairs

题意

f(s,t)f(s,t)f(s,t)为最大的iii使得s1...i=t∣t∣−i+1...∣t∣s_{1...i} =t_{\left|t\right|-i+1...\left|t\right|}s1...i=tti+1...t
nnn个串s1,s2,...,sns_1,s_2,...,s_ns1,s2,...,sn,求∑i=1n∑j=1nf(si,sj)2\displaystyle\sum_{i = 1} ^ n\displaystyle\sum_{j = 1} ^ n f(s_i,s_j)^2i=1nj=1nf(si,sj)2

题解

统计所有串每一个后缀出现次数,这个可以用哈希来实现

map<ull, int> mp;
void insert(string &s) {
    ull hash = 0, b = 1;//unsigned long long就可以自然溢出
    for (int i = s.length() - 1; i >= 0; i--, b *= base) {
        hash += b * (s[i] - 'a' + 1);
        mp[hash]++;
    }
}

对于一个串sss来说,记cnt[i]cnt[i]cnt[i]为所有串中后缀等于s1...is_{1...i}s1...i的数量
那么串sss的贡献就是∑i=1∣s∣i2cnt[i]\displaystyle\sum_{i=1}^{|s|}i^2cnt[i]i=1si2cnt[i]

这里有一个问题,如果我们按上面HashHashHash的方法来求得所有串每一个后缀出现次数
会有重复计算,如abaabaaba的后缀有a,ba,abaa,ba,abaa,ba,aba, 如果我们当前串sss能够匹配的最大长度为333,即匹配的串为abaabaaba时, aaa这个串也一定是匹配的,所以要对cntcntcnt进行容斥
容斥只需要从前往后令cnt[next[i]]=cnt[next[i]]−cnt[i]cnt[next[i]] = cnt[next[i]]-cnt[i]cnt[next[i]]=cnt[next[i]]cnt[i]即可

因为如果从后往前容斥就要不断跳nextnextnext数组,前面每一个重复的串都要减去cnt[i]cnt[i]cnt[i]的贡献
但是正向进行就只用减一次,因为cnt[next[i]]=cnt[next[i]]−cnt[i]cnt[next[i]] = cnt[next[i]]-cnt[i]cnt[next[i]]=cnt[next[i]]cnt[i]中,cnt[i]cnt[i]cnt[i]本就包含了后面重复的贡献

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAX = 1e5 + 10;
const int base = 233;
const int mod = 998244353;

vector<int> getNext(string &s) {
    int n = s.length();
    vector<int> nxt(n);
    for (int i = 1; i < n; i++) {
        int j = nxt[i - 1];
        while (j > 0 && s[i] != s[j]) j = nxt[j - 1];
        if (s[i] == s[j]) j++;
        nxt[i] = j;
    }
    return nxt;
}

map<ull, int> mp;
void insert(string &s) {
    ull hash = 0, b = 1;//unsigned long long就可以自然溢出
    for (int i = s.length() - 1; i >= 0; i--, b *= base) {
        hash += b * (s[i] - 'a' + 1);
        mp[hash]++;
    }
}

int N;
string s[MAX];
int cnt[MAX * 10];

int main() {

    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> s[i];
        insert(s[i]);
    }
    ll ans = 0;
    for (int i = 1; i <= N; i++) {
        vector<int> nxt = getNext(s[i]);
        ull hash = 0;
        for (int j = 0; j < s[i].length(); j++) {
            hash = hash * base + s[i][j] - 'a' + 1;
            cnt[nxt[j]] -= (cnt[j + 1] = mp[hash]);
            //我这里j是从0开始的, 但是cnt数组是从1开始的,有点不一样
        }
        for (int j = 1; j <= s[i].length(); j++)
            ans = (ans + 1ll * cnt[j] * j % mod * j % mod) % mod;
    }
    printf("%lld\n", ans);


    return 0;
}
基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样与统计,通过模拟系统元件的故障与修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构与设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码与案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行与可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理与实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估与优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更不确定性因素进行深化研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值