您的位置:首页 > 新闻 > 资讯 > 暑假计算机培训班有哪些_厦门企业建网站制作_夫唯seo教程_南通百度seo代理

暑假计算机培训班有哪些_厦门企业建网站制作_夫唯seo教程_南通百度seo代理

2024/10/12 20:13:44 来源:https://blog.csdn.net/dakingffo/article/details/142798877  浏览:    关键词:暑假计算机培训班有哪些_厦门企业建网站制作_夫唯seo教程_南通百度seo代理
暑假计算机培训班有哪些_厦门企业建网站制作_夫唯seo教程_南通百度seo代理

目录

概述

核心概念:z[i]

思路

算法过程

Code


概述

LeetCode 2223:

你需要从空字符串开始 构造 一个长度为 n 的字符串 s ,构造的过程为每次给当前字符串 前面 添加 一个 字符。构造过程中得到的所有字符串编号为 1 到 n ,其中长度为 i 的字符串编号为 si 。

  • 比方说,s = "abaca" ,s1 == "a" ,s2 == "ca" ,s3 == "aca" 依次类推。

si 的 得分 为 si 和 sn 的 最长公共前缀 的长度(注意 s == sn )。

给你最终的字符串 s ,请你返回每一个 si 的 得分之和 。

示例 1:

输入:s = "babab"
输出:9
解释:
s1 == "b" ,最长公共前缀是 "b" ,得分为 1 。
s2 == "ab" ,没有公共前缀,得分为 0 。
s3 == "bab" ,最长公共前缀为 "bab" ,得分为 3 。
s4 == "abab" ,没有公共前缀,得分为 0 。
s5 == "babab" ,最长公共前缀为 "babab" ,得分为 5 。
得分和为 1 + 0 + 3 + 0 + 5 = 9 ,所以我们返回 9 。

Z函数是一种独到的匹配算法,虽然国内称之为扩展KMP,其实他更具有Manacher算法的特征

与KMP类似的是,它也是字符串自匹配问题:

KMP_next:next[i]表示[0,i-1]子串的最长公共前后缀长度。

Z_function:z[i]表示[0,n)完整串与[i,n)子串的最长公共前缀长度(即LCP)。

Z函数基于主串与他的后缀串[i,n)大做文章。


核心概念:z[i]

Z_function:z[i]表示[0,n)完整串与[i,n)子串的最长公共前缀(即LCP)。

我们来形象理解一下这句话:

       i    0 1 2 3 4 5 6 
char s[i] = -------------└str┘---------└str┘
对于从i=2开始的子串,其与主串有相同的前缀str,长度为3,故z[2]=3

具体地,

       i    0 1 2 3 4 5 6 
char s[i] = a b a c a b a
int  z[i] = 0 0 1 0 3 0 1

*注意*:约定z[0]=0。 


思路

LeetCode 2223:题意很明显,就是要我们求这个主串的z函数,然后累加求和。

 暴力的做法很容易想到,但我们提供一种更优越的做法:Z-box操作。

我们上文提到z函数很像manacher算法,也正因如此,这是一种维护已知量使得时间复杂度到达线性级别的算法。


算法过程

接下来我们称最长公共前缀为LCP。

我们维护Z-box区间l和r双指针,初始值为l=r=0,

我们设计Z-box总是维护已知的最大子串LCP的两端

       i    0 1 2 3 4 5 6 7
char s[i] = ---------------|s[2,n)     -----------└Z-box┘l     r
int  z[i] = 0 2 4 ?
遍历到i=4时,此前已知的最长z[2]=4
故Z-box维护这个区间,有l=2,r=5;

考虑当我们遍历到下标i时,若其处于z-box中(l<=i<=r),那么它可以对应到原始主串的i-r下标处。

这是因为z[i]表示[0,n)完整串与[i,n)子串的LCP,而z-box维护最长z[i]的端点l和r,那么对于l<=i<=r,s[i]==s[i-l]。

因此我们就可以直接利用此前已知的z函数值推断未知值。

有两种情况(其中一种有三种小情况)需要讨论:

1.i<=r,此时z[i]至少>= min(z[i - l], r - i + 1);  

有以下三种小情况,观察下图,结合z函数定义,注意* ^ ~这三个位置: 

①z[i-l]<r-i+1,即i对应点最长延伸严格小于Z-box对应区的右边界

i=3时,注意* ^ ~这三个位置  i          3 |
char s[i] = ---------------└z[j]┘*└z[j]┘^    //j=i-l;└-Z-box-┘s[2,n)     -----------└z[i]┘~└-Z-box-┘l       r
*与^必不同
^与~必相同
故*与~必不同,此时z[i]=z[i-l];

此时z[i]=z[i-l];

②z[i-l]=r-i+1,即i对应点最长延伸恰好压线Z-box对应区的右边界

注意* ^ ~这三个位置i          3|
char s[i] = ---------------└-z[j]┘*└-z[j]┘^    //j=i-l;└-Z-box-┘s[2,n)     -----------└-z[i]┘~└-Z-box-┘l       r
*与^必不同
^与~必不同
^与~可能相同,z[i]>=z[i-l];

此时z[i]=z[i-l],随后暴力扩展z[i]; 

③z[i-l]>r-i+1

注意* ^ ~这三个位置i          3|
char s[i] = ---------------└-z[j]-┘*└-z[j]-┘    //j=i-l;└-Z-box-┘^s[2,n)     -----------└z[i]-┘~└-Z-box-┘l       r
*与^必相同
^与~必不同
故*与~必不同,此时被截断一部分,z[i]=r - i + 1;

此时z[i]=r - i + 1; 

2.i>r

不用看图,这种情况直接暴力扩展z[i]即可。

综合以上情况,我们能写下以下代码:

vector<int> Z_algorithm(const string& s) {const int n = s.size();vector<int> z(s.size(), 0);for(int i = 1, l = 0, r = 0; i < n; i++) {if (i <= r)z[i] = min(z[i - l], r - i + 1);//i<=r时执行操作while (i + z[i] < n && s[z[i]] == s[i + z[i]])z[i]++;//暴力扩展if (i + z[i] - 1 > r)l = i, r = i + z[i] - 1;//更新Z-box}return z;
}

*注意*:我们在代码层面虽然对于任意的i都会进入暴力扩展while循环,但对于不需要暴力的情况,while会直接跳出。


Code

class Solution {
public:vector<int> Z_algorithm(const string& s) {const int n = s.size();vector<int> z(s.size(), 0);for (int i = 1, l = 0, r = 0; i < n; i++) {if (i <= r)z[i] = min(z[i - l], r - i + 1);while (i + z[i] < n && s[z[i]] == s[i + z[i]])z[i]++;if (i + z[i] - 1 > r)l = i, r = i + z[i] - 1;}return z;}long long sumScores(string s) {vector<int>&& ans = Z_algorithm(s);ans[0] = s.size();return accumulate(ans.begin(), ans.end(), 0ll);}
};

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com