目录
- [3042. 统计前后缀下标对 I 简单](#3042. 统计前后缀下标对 I 简单)
- [3043. 最长公共前缀的长度 中等](#3043. 最长公共前缀的长度 中等)
- [3044. 出现频率最高的质数 中等](#3044. 出现频率最高的质数 中等)
- [3045. 统计前后缀下标对 II 困难](#3045. 统计前后缀下标对 II 困难)
3042. 统计前后缀下标对 I 简单
分析:
数据量较少,直接暴力即可,也可以采用 构建前后缀字典树(3045. 统计前后缀下标对 II)。
代码:
cpp
class Solution {
public:
int countPrefixSuffixPairs(vector<string>& words) {
int n=words.size(), ans=0;
for(int i=0;i<n;i++){
int l = words[i].length();
for(int j=i+1;j<n;j++){
int le = words[j].length();
if(l>le) continue;
if(words[i]==words[j].substr(0,l) && words[i]==words[j].substr(le-l,l)) ans++;
}
}
return ans;
}
};
3043. 最长公共前缀的长度 中等
分析:
对 arr1
构建字典树,在遍历 arr2
进行比较即可。
代码:
cpp
class Node{
public:
int val;
vector<Node*> ne;
Node() : val(0), ne(10, nullptr) {}
Node(int v) : val(v), ne(10, nullptr) {}
};
class Solution {
public:
void buildTree(Node* root, string& s, int i){
if(i>= s.length()) return;
int k = s[i] - '0';
if(root->ne[k] == nullptr){
Node *node = new Node(s[i]-'0');
root->ne[k] = node;
}
buildTree(root->ne[k], s, i+1);
}
int longestCommonPrefix(vector<int>& arr1, vector<int>& arr2) {
Node* root = new Node();
for(int& a : arr1){
string s = to_string(a);
buildTree(root, s, 0);
}
int ans=0;
for(int& a : arr2){
string s = to_string(a);
int i=0;
Node* p=root;
for(;i<s.length();i++){
int k = s[i]-'0';
if(p->ne[k]==nullptr) break;
p=p->ne[k];
}
ans=max(i,ans);
}
return ans;
}
};
3044. 出现频率最高的质数 中等
分析:
选择合适的判断质数的算法,按照题意模拟即可。
注意: 如果对数据范围内的质数进行打表,需要注意调用方式,否则会出现多次计算,最终超时。
代码:
对质数打表,再判断。
cpp
const int ma = 1e7+5;
vector<int> prime(ma,1);
void getPrimes(){
prime[0]=prime[1]=0;
for(int i=2;i<ma;i++){
if(prime[i]==0) continue;
for(int j=2;j*i<ma;j++) prime[j*i]=0;
}
for(int i=2;i<10;i++) prime[i]=0;
}
void init(){
static bool ini = false;
if(!ini){
ini = true;
getPrimes();
}
}
class Solution {
public:
int mostFrequentPrime(vector<vector<int>>& mat) {
init();
int n=mat.size(), m=mat[0].size(), ans=-1, mm=0;
unordered_map<int,int> mp;
int move_x[] = {0,1,1,1,0,-1,-1,-1}, move_y[] = {1,1,0,-1,-1,-1,0,1};
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
for(int k=0;k<8;k++){
int x=i,y=j;
int m_x=move_x[k], m_y=move_y[k], t=mat[i][j];
x+=m_x;y+=m_y;
while(x>=0&&y>=0&&x<n&&y<m){
t=t*10+mat[x][y];
if(prime[t]){
mp[t]++;
if(mm<mp[t]){
ans=t;
mm=mp[t];
}else if(mm==mp[t]){
ans=max(ans,t);
}
}
x+=m_x;y+=m_y;
}
}
}
}
return ans;
}
};
对每一个出现的数进行质数的判断。
cpp
class Solution {
public:
bool isPrime(int n){
if(n<10) return false;
for(int i=2;i*i<=n;i++){
if(n%i==0) return false;
}
return true;
}
int mostFrequentPrime(vector<vector<int>>& mat) {
int n=mat.size(), m=mat[0].size(), ans=-1, mm=0;
unordered_map<int,int> mp;
int move_x[] = {0,1,1,1,0,-1,-1,-1}, move_y[] = {1,1,0,-1,-1,-1,0,1};
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
for(int k=0;k<8;k++){
int x=i,y=j;
int m_x=move_x[k], m_y=move_y[k], t=mat[i][j];
x+=m_x;y+=m_y;
while(x>=0&&y>=0&&x<n&&y<m){
t=t*10+mat[x][y];
if(isPrime(t)){
mp[t]++;
if(mm<mp[t]){
ans=t;
mm=mp[t];
}else if(mm==mp[t]){
ans=max(ans,t);
}
}
x+=m_x;y+=m_y;
}
}
}
}
return ans;
}
};
3045. 统计前后缀下标对 II 困难
分析:
对于前缀,可以通过构建 字典树Trie ,进行判断。
而后缀,观察字符串 S = "abcabc"
和 s1 = "abc"
的前后缀情况:
S
的前后缀与s1
一致,即S[0...2]
和S[3...5]
均与s1
相等。
S[0], S[5] = a, c
,s1[0], s1[2] = a, c
S[1], S[4] = b, b
,s1[1], s1[1] = b, b
S[2], S[3] = c, a
,s1[2], s1[0] = c, a
易得若
S
(长度为L
)的前后缀与s1
(长度为l
)完全一致则有:
S[0], S[L-1] == s1[0], s1[l-1]
S[1], S[L-2] == s1[1], s1[l-2]
- ...
S[l-1], S[L-l+1] == s1[l-1], s1[0]
根据得到的性质,在构建字典树时,同时记录前缀与后缀的字符,再进行对比即可。
注意:
i < j
,因此不需要根据字符串长度进行排序。
也可以正常构建字典树来对比前缀,同时使用 **Z函数(拓展KMP)**来对比后缀。
代码:
cpp
struct Node {
unordered_map<int, Node*> son;
int cnt=0;
};
class Solution {
public:
long long countPrefixSuffixPairs(vector<string>& words) {
long long ans=0;
Node *root = new Node();
for(string& s : words){
int n = s.length();
Node *cur = root;
for(int i=0;i<n;i++){
int p = (int) ((s[i] - 'a') << 5) | (s[n - i - 1] - 'a'); // 仅26个字母,五位二进制即可完全表示,使用十位二进制数来表示(前五位表示前缀,后五位表示后缀)
if(cur->son[p] == nullptr){
cur->son[p] = new Node();
}
cur = cur->son[p];
ans += cur->cnt;
}
cur->cnt++;
}
return ans;
}
};