简单来说这玩意就是对一个长度为n的字符串s,计算s的从第i个字符开始的后缀和原字符串s的最长公共子串(i = 1,2,……n),然后拿这个东西来计算别的东西的
知识点可以参考:Z 函数(扩展 KMP) - OI Wiki
代码如下:
#ifndef __ZFUNCTION__
#define __ZFUNCTION__
#include<vector>
#include<string>
#include<algorithm>
struct ZFunction{
std::string s;
std::vector<int> z;
void getZ(void);
ZFunction(){}
ZFunction(std::string s):s(s){this->getZ();}
std::vector<int> matchSubString(const std::string &p);
int cntDiffSubstring(void);
int getPeriod(void);
};
void ZFunction::getZ(void){
int n = s.size(); z.resize(n, 0);
for(int i = 1, l = 0, r = 0; i < n; i++){
if(i <= r && z[i - l] <= r - i + 1) z[i] = z[i - l];
else{
z[i] = std::max(0, 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;
}
}
std::vector<int> ZFunction::matchSubString(const std::string &p){
ZFunction tmp(p + "\0" + s); std::vector<int> ans; int len = p.size();
for(int i = 0; i < s.size(); i++)
if(z[i + len + 1] == len) ans.push_back(i);
return ans;
}//检测p在s中出现的位置(其中p和s中不能有'\0',如果有,可以按需修改)
int ZFunction::cntDiffSubstring(void){
std::string s_t; int ans = 0;
for(int i = 0; i < s.size(); i++){
s_t = s[i] + s_t;
ZFunction z_t(s_t);
int maxn = 0;
for(int i = 0; i < z_t.z.size(); i++)
maxn = std::max(maxn, z_t.z[i]);
ans += s_t.size() - maxn;
}
return ans;
}//计算s的本质不同子串数
int ZFunction::getPeriod(void){
int n = z.size();
for(int i = 0; i < n; i++)
if(i + z[i] == n) return i;
return n;
}//计算字符串整周期
#endif
其实我写这个东西,一来是为了复习一下知识点,看看自己能不能复现Z函数
二来是为了缓解学习数据分析的苦闷(准确来说是学numpy和pandas),没有可以拿来练手的东西看着真的很困