提高字符串习题

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了提高字符串习题相关的知识,希望对你有一定的参考价值。

提高字符串习题

1.P1039 [NOIP2003 提高组] 侦探推理

tag:模拟&字符串&暴力

只需确定两个东西:罪犯是谁,今天星期几。

m × 7 m\\times 7 m×7 枚举答案,就可以判断所有是否说假话真话。

本地的坑:

1.字符串特判。

2.不说话的人 可以真也可以假。

所以最后 n n n落在指定的范围内即可。

参考别人Code

#include<iostream>
#include<map>
#include<vector>
#include<cstdio>
using namespace std;
map<string,int> per;//存人名
string nm[25];
map<string,int> day;//映射日期
struct sta
{
    int u;//u表示主语
    bool to;//0表示罪犯,1表示日期
    bool is;//表示肯定或否定
    sta(int u,bool to,bool is)
    {
        this->u=u;
        this->to=to;
        this->is=is;
    }
    sta(){}
};
vector<sta> v[25];
char asdfghjkl[1000];//用来读废掉的语句
int main()
{
    int n,m,p;
    cin>>n>>m>>p;
    string s;
    for(int i=1;i<=n;++i)
    {
        cin>>s;
        per[s]=i;
        nm[i]=s;
    }
    per["Today"]=n+1;
    day["Monday."]=1;//句号是因为答案
    day["Tuesday."]=2;
    day["Wednesday."]=3;
    day["Thursday."]=4;
    day["Friday."]=5;
    day["Saturday."]=6;
    day["Sunday."]=7;
    for(int i=1;i<=p;++i)
    {
        cin>>s;
        s=s.substr(0,s.size()-1);//自动去掉
        int t=per[s];
        cin>>s;
        int u=per[s];
        if(u<=n)//表示人名
        {
            cin>>s;
            if((u&&s!="is")||(!u&&s!="am"))
            {
                gets(asdfghjkl);
                continue;
            }
            if(!u)
                u=t;
            cin>>s;
            if(s=="not")
            {
                cin>>s;
                if(s=="guilty.")
                    v[t].push_back(sta(u,0,0));
            }
            else if(s=="guilty.")
                v[t].push_back(sta(u,0,1));
        }
        else if(u==n+1)//表示日期
        {
            cin>>s;
            if(s!="is")
                continue;
            cin>>s;
            if(day[s])
                v[t].push_back(sta(day[s],1,1));
        }
        else
            gets(asdfghjkl);
    }
    string ans="";
    //枚举谁是罪犯
    for(int i=1;i<=n;++i)
    {
        //枚举今天星期几
        for(int j=1;j<=7;++j)
        {
            int flag=0,cnt=n,ran=0;//ran表示波动范围
            for(int k=1;!flag&&k<=n;++k)
            {
                vector<sta>::iterator it=v[k].begin();
                if(!v[k].size())
                {
                    ++ran;
                    continue;
                }
                sta tmp=*it;
                bool rea;
                if(tmp.to)
                    rea=(tmp.u==j);
                else
                    rea=((tmp.u==i)^(!tmp.is));
                ++it;
                for(;!flag&&it!=v[k].end();++it)
                {
                    if(it->to)
                    {
                        if(rea!=(it->u==j))
                            flag=1;
                    }
                    else
                    {
                        if(rea==((it->u==i)^it->is))
                            flag=1;
                    }
                }
                cnt-=rea;
            }
            if(!flag&&cnt>=m&&cnt-ran<=m)
            {
                if(ans=="")
                    ans=nm[i];
                else if(ans!=nm[i])
                {
                    cout<<"Cannot Determine"<<endl;
                    return 0;
                }
            }
        }
    }
    if(ans=="")
        cout<<"Impossible"<<endl;
    else
        cout<<ans<<endl;
    return 0;
}

2.P1368 【模板】最小表示法

应用:找出字符串S的的循环同构串中字典序最小的一个

那么什么是循环同构串呢?

我们设S= “bcad” ,且设S’是S的循环同构的串。那么S’可以是 “bcad” 或者 “cadb” , “adbc” , “dbca”

即在字符串S中从i>=0开始,从i循环到字符串末尾,再从头循环到i,所形成的字符就是S循环同构串。

又因为这样的同构串不止一个,所以我们要找出其中字典序最小的一个即为S的最小表示(即字符串从小到大排序,其中字典序最小的一个)

其实还是有最大表示法的,但本题从左到右进行操作,所以我们使用最小表示法即可

最小表示法其实就是找到位置i,从这个位置输出S,使得到的同构串字典序最小

就是利用两个指针和单调性,将不可能的一段区间快速跳过。

可以使用倍长或者取模,但是倍长时间上效率更高。

该算法的优点是复杂度是: O ( n ) O(n) O(n)

缺点是扩展性较差,应用范围小。

code

// Problem: P1368 【模板】最小表示法
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1368
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// Date: 2021-10-26 20:07:42
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=6e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
int n,a[N];
int solve(){
	int i,j=1,k;
	i=k=0;
	while(i<n&&j<n&&k<n){
		if(a[i+k]==a[j+k]) k++;
		else {
			if(a[i+k]>a[j+k]) i+=k+1;
			else j+=k+1;
			if(i==j) i++;
			k=0;
		}
	}
	return min(i,j);
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%d",&a[i]),a[n+i]=a[i];
	int pos = solve();
	int r=pos+n;
	for(int i=pos;i<r;i++) printf("%d ",a[i]);
	return 0;
}

最大表示法(求字典序最大) 就是把 跳指针那块反着写即可。下面来个取模版本。

int solve(){
	int i,j=1,k;
	i=k=0;
	while(i<n&&j<n&&k<n){
		int x=a[(i+k)%n]-a[(j+k)%n];
		if(!x) k++;
		else {
			if(x>0) j+=k+1;
			else i+=k+1;
			if(i==j) i++;
			k=0;
		}
	}
	return min(i,j);
}

3.P6286 [COCI2016-2017#1] Cezar

字符串+拓扑

按照 A A A数组对字符串进行比较,然后对字母建边,跑拓扑输出答案即可。

值的注意是:当一个字符串是另一个串的前缀时,如果前者在 A A A中靠后直接输出无解。

然后就是拓扑中有环也是无解。

#include <bits/stdc++.h>
using namespace std;
string s[1001];
int sum,vis[1001],viss[1001],ans[20010],ans2[20010];
int n,tot,cnt,a[1001],in[1001],head[200010],anss[20010];

struct node {
	int to,net;
} e[200010];

void add(int u,int v) {
	e[++tot].to=v;
	e[tot].net=head[u];
	head[u]=tot;
}

int topo() {
	queue<int> q;
	for(int i=0;i<=25;i++) {
		if(!in[i]&&vis[i]) q.push(i);
	}
	if(!q.size()) return -1;
	while(!q.empty()) {
		bool flag=false;
		int x=q.front();
		q.pop();
		if(viss[x]) return -1;
		viss[x]=1;
		ans[++sum]=x;
		ans2[sum]=x;
		for(int i=head[x];i;i=e[i].net) {
			int v=e[i].to;
			if(--in[v]==0) {
				flag=true;
				q.push(v);
			}
		}
	}
	return sum;
}


int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		cin>>s[i];
	}
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
	}
	for(int i=1;i<n;i++) {
		int k=0,kk=0;
		while(k<s[a[i]].size()&&kk<s[a[i+1]].size()) {
			if(s[a[i]][k]!=s[a[i+1]][kk]) {
				add(s[a[i]][k]-'a',s[a[i+1]][kk]-'a');	
				if(!vis[s[a[i]][k]-'a']) cnt++,vis[s[a[i]][k]-'a']=1;
				if(!vis[s[a[i+1]][kk]-'a']) cnt++,vis[s[a[i+1]][kk]webstorm代码片段的创建

Python函数练习题

Sublime Text自定制代码片段(Code Snippets)

Python3练习题系列(03)

前端开发必备!Emmet使用手册

第四章课后习题