MC0210安全验证(码蹄集)

码题集OJ-安全验证 (matiji.net)

题面:

题意:给定一个字符串s,找出最长的子串t,满足前后缀都为t,且中间段还出现了t(中间段:除了第一个与最后一个字符)

第一个样例就是fix出现了在前缀,也出现了在后缀,中间段也能找到fix,且fix为最长满足要求的字串,故答案输出fix

思路1:

1.一个朴素的做法就是,从大到小枚举字符串长度,设当前枚举的字符串长度为len,则前缀字符串为t1(截取区间[1,len]) ,   后缀字符串为t2 ( 截取区间[n-len+1,n],n为字符串长度),判断t1是否等于t2,不等于的话减小枚举长度,等于的话,再判断中间段是否出现了t1(t2,此时t1等于t2)

2.判断中间段是否出现了t1,可以暴力判断,但是此时时间复杂为o(n*m),可以用find函数优化(java/c++/pythoon,find查找字符串时都是o(n)的时间复杂度),此时的总时间复杂度会降为o(n^2)

3.n^2的时间复杂度,理论上是不可以过的,不过这里可以试着先提交,提交发现你会发现此时就可以Ac了,先贴上一个代码吧,

AC_Code:C++(时间复杂度n^2)(理论会上会tle,但是可以过码题集上的所有数据)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include<stack>
#include<cmath>
#include <unordered_set>
#include <unordered_map>
#include<set>
#include <map>


using namespace std;

typedef long long LL;
typedef pair<int,int>PII;

#define x first
#define y second
#define ls u<<1
#define rs u<<1|1
#define all(ss) ss.begin(),ss.end()

int const mod1=998244353;   
int const mod2=1e9+7;
int const N=2e5+7;
int const INF=0x3f3f3f3f;

int T;
int n,m;
string s;

//检查前后缀是否相等
bool check(int len){
	for(int i=1,j=n-len+1;i<=len;i++,j++)
		if(s[i]!=s[j])	return false;
	return  true;
}

void solve(){
    cin>>s;
    n=s.size();	
    s=' '+s;	//让下标从1开始
    
    string mid;	//除去首尾字符后剩下的中间字符
    for(int i=2;i<=n-1;i++)	mid+=s[i];
    
    int len=n-2;
    string ans;	//答案
    for(int i=1;i<=len;i++)	ans+=s[i];
    
    while(len>0){	//长度小于2不会进入while循环
		if(!check(len)){	//前缀不等于后缀	
			ans.pop_back();
			len--;
			continue;
		}
		
		if((int)mid.find(ans)!=-1){	//查找中间是否有ans字符串
			cout<<ans<<endl;
			return;	//找到了直接输出答案,结束程序
		}
		
		ans.pop_back();
		len--;
	}
	
	puts("No");
} 

void init(){                     
    
}

int main()
{
    //std::ios::sync_with_stdio(false);   cin.tie(0); cout.tie(0);
    T=1;
    //cin>>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;
}

对find函数感兴趣,看这里c++的find函数介绍

思路2:hash字符串+二分优化

1.换过角度思考,我们可以预处理出,前后缀相等的所有合法的长度,此时可以用hash字符串,o(n)的时间复杂度可以预处理出所有前后缀相等的长度

2.有了所有前后缀相等的合法长度,此时一个暴力的做法就是从大到小枚举长度,判断是否合法

3.但是这个方法理论上又是会tle的

一个重要的性质:再所有合法的长度中,如果存在一个字符串t1,其中间段能找到一个与t1相等的字符串p1,那么一个比t1短的字符串t2,中间段也一定能找到一个与t2相等的字符串p2。那么此时我们就可以考虑二分所有的合法长度了。

此处敲三下黑板:红字部分是重点,没懂,多看几十遍

AC_Code:C++(时间复杂度nlogn)

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
int const N=1e6+7;
int const base=131,mod=1e9+7;

int T;
int n,m;
vector<int>a;	//存储前后缀相等的长度
char s[N];
LL hash1[N],p[N];   //hash1为hash数组,p为进制数

//模板
LL get(int l,int r){
	return ((hash1[r]-hash1[l-1]*p[r-l+1])%mod+mod)%mod;
}

//检查当前长度是否合法
bool check(int len){
	LL target=get(1,len);
	for(int i=2;i+len-1<=n-1;i++)
		if(get(i,i+len-1)==target)
			return true;
	return false;
}

void solve(){
    scanf("%s",s+1);
    n=strlen(s+1); //有效字符[1,n]
    
    //hash字符串
    p[0]=1;
    for(int i=1;i<=n;i++){
		hash1[i]=(hash1[i-1]*base+s[i])%mod;
		p[i]=(p[i-1]*base)%mod;
	}
	
	//预处理出前缀等于后缀的所有长度
	for(int len=1;len<=n-2;len++)
		if(get(1,len)==get(n-len+1,n))
			a.push_back(len);
	
	//二分最大长度
	int max_len=0;
	int l=0,r=a.size()-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(a[mid]))	max_len=a[mid],l=mid+1;
		else r=mid-1;
	}
	
	//输出答案
	if(max_len)	for(int i=1;i<=max_len;i++)	putchar(s[i]);
	else puts("No");
	
	
} 

void init(){                     
    
}

int main()
{
    //std::ios::sync_with_stdio(false);   cin.tie(0); cout.tie(0);
    T=1;
    //cin>>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;	
}

思路3:扩展KMP算法(又称z函数)

z函数:z[i]表示以i开头的后缀与前缀相等的最长长度

这里默认下标从0开始,z[0]默认为0

z(abcabcabc)=[0,0,0,6,0,0,3,0,0]

z(aacaa)=[0,1,0,2,1]

z(aacaadaa)=[0,1,0,2,1,0,2,1]

想彻底学会z函数的请到这里Z 函数(扩展 KMP) - OI Wiki

z函数模板:时间复杂度是o(n)的

​
vector<int> z_function(string s) {
  int n = (int)s.length();
  vector<int> z(n);
  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] = 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;
  }
  return z;
}

​

1.z函数可以直接获得从任意一个字符开始与前缀相同的最长长度值。

2.先预处理出来z函数,找出z函数数组的最大值max_len(其实就是与前缀相同的最大值),那么所有的前后缀相等的情况一定被这个最大值所包含,那么只要你的长度小于max_len,那么中间就一定会出现子串t

3.还有一种情况就是长度等于max_len的情况也可以,如s="aacaadaa",此时max_len=2,最长前后缀为aa,但是中间也出现aa那么答案就是aa了

4.s="abcabcabc",此时前后缀相等的情况有abc,abcabc两种情况,max_len=6,此时的abcabc要么包含前缀,要么包含后缀都是不合法的,中间没有出现abcabc,故答案只能为abc

5.红色部分的字是关键,看不懂,就多看几遍,多看几遍不懂,记得看z函数的定义,

AC_Code:C++(时间复杂度n)

#include <iostream>
#include<vector>

using namespace std;

int T;
int n,m;


//模板
vector<int> z_function(string s) {
  int n = (int)s.length();
  vector<int> z(n);
  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] = 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;
  }
  return z;
}

void solve(){
    string s; cin>>s;	
    n=s.size();
    vector<int>z=z_function(s);
    
    //可以打印观察z函数
//    for(int i=0;i<n;i++)	cout<<z[i]<<' ';
//    cout<<endl;
    
    //max_len最大等于前缀的长度
    //max_cnt最大等于前缀的长度的个数
    int max_len=0,max_cnt=0;	
    int ans=0; //前后缀相等的最大长度
    for(int i=0;i<n;i++){
		max_len=max(max_len,z[i]);
		if(i+z[i]==n)	//前缀等于后缀
			ans=max(ans,z[i]);	
	}
		
    for(int i=0;i<n;i++)	//统计等于最大前缀出现的次数
		max_cnt+=max_len==z[i];
    
	if(ans&&ans==max_len&&max_cnt>=2) {	//存在前后缀相等,且中间段也出现过的情况
		for(int i=0;i<ans;i++)	putchar(s[i]);
	}
	else{
		ans=0;
		for(int i=0;i<n;i++)
			if(max_len!=z[i]&&i+z[i]==n)	//除了max_len,找最大值
				ans=max(ans,z[i]);
		
		if(ans)	for(int i=0;i<ans;i++)	putchar(s[i]);
		else puts("No");
	}

} 

void init(){                     
    
}

int main()
{
    //std::ios::sync_with_stdio(false);   cin.tie(0); cout.tie(0);
    T=1;
    //cin>>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
要使用mc工具修改minio密码,可以按照以下步骤进行操作: 1. 打开命令行终端,并导航到mc工具的安装目录。 2. 输入以下命令来修改minio的密码: ``` mc config host add minio http://192.168.0.112:9005 minioadmin minioadmin ``` 这将使用明文方式输入新的minio密码。 或者,你也可以使用密文方式输入新的minio密码,命令如下: ``` mc config host add minio http://192.168.0.112:9005 Enter Access Key: minioadmin Enter Secret Key: minioadmin ``` 3. 运行以上命令后,mc工具会将新的minio密码保存在配置文件中。 4. 现在,你可以使用mc工具来管理minio存储服务,并使用新的密码进行身份验证。 请注意,以上步骤中的minio地址和密码仅作为示例,请根据你的实际情况进行相应的修改。 #### 引用[.reference_title] - *1* [Minio](https://blog.csdn.net/weixin_48360967/article/details/126572067)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [windows minio 修改密码修改 MINIO_ACCESS_KEY minio开机启动](https://blog.csdn.net/weixin_43853737/article/details/109637944)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值