数据结构第六周 :(公共钥匙盒 + 等值子串 + KMP模式匹配 + 大整数相乘 + 最长公共子串 + 匹配串 + 最长回文子串 + 年号字串)

公共钥匙盒——队列

【问题描述】

有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家。每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中。

钥匙盒一共有N个挂钩,从左到右排成一排,用来挂N个教室的钥匙。一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。

每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。

今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。有K位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的?

【输入形式】

输入的第一行包含两个整数N, K。

接下来K行,每行三个整数w, s, c,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。可能有多位老师使用同一把钥匙,但是老师使用钥匙的时间不会重叠。

保证输入数据满足输入格式,你不用检查数据合法性。

【输出形式】

输出一行,包含N个整数,相邻整数间用一个空格分隔,依次表示每个挂钩上挂的钥匙编号。

【样例输入】

5 2

4 3 3

2 2 7

【样例输出】

1 4 3 2 5

【样例说明】

第一位老师从时刻3开始使用4号教室的钥匙,使用3单位时间,所以在时刻6还钥匙。第二位老师从时刻2开始使用钥匙,使用7单位时间,所以在时刻9还钥匙。

每个关键时刻后的钥匙状态如下(X表示空):

时刻2后为1X345;

时刻3后为1X3X5;

时刻6后为143X5;

时刻9后为14325。

【样例输入】

5 7

1 1 14

3 3 12

1 15 12

2 7 20

3 18 12

4 21 19

5 30 9

【样例输出】

1 2 3 5 4

【评分标准】

对于30%的评测用例,1 ≤ N, K ≤ 10, 1 ≤ w ≤ N, 1 ≤ s, c ≤ 30;

对于60%的评测用例,1 ≤ N, K ≤ 50,1 ≤ w ≤ N,1 ≤ s ≤ 300,1 ≤ c ≤ 50;

对于所有评测用例,1 ≤ N, K ≤ 1000,1 ≤ w ≤ N,1 ≤ s ≤ 10000,1 ≤ c ≤ 100

#include <iostream>
#include <queue>
#include<stdio.h>
using namespace std;
const int nmax=100;
int hook[nmax+1];

struct node{
    int num;
    char op; //操作:'L'-取钥匙lend,'R'-还钥匙return
    int timep;//时间点

    bool operator<(const node& rhs)const {
        if(timep!=rhs.timep){
            return timep>rhs.timep;//如果多个人先后还,优先输出借书时间小的
        }
        else if(op!=rhs.op){//如果同一时刻既有人还,又有人借,先全部还书再借
            return op<rhs.op;//同一时刻既有还又有借,优先输出还的,即字符值大的
        }
        else{//如果多个人同时还,优先输出钥匙编号小的
            return num>rhs.num;
        }
    }
};
int main() {
    priority_queue<node>q;
    node nd;
    int n,m;//n为hook数组的长度,m次操作
    int num,start,len;//num一位老师要使用的钥匙编号、start开始上课的时间和上课的时长len
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        hook[i]=i;//先初始化各个钥匙
    }

    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&num,&start,&len);
        nd.num=num;
        nd.timep=start;
        nd.op='L';
        q.push(nd);

        nd.op='R';
        nd.timep=start+len;
        q.push(nd);
    }
    while(!q.empty()){
        nd=q.top();
        q.pop();
        if(nd.op=='L'){
            for(int i=1;i<=n;i++){
                if(hook[i]==nd.num){//如果hook数组里有要取走的钥匙
                    hook[i]=0;
                    break;
                }
            }
        }
        else {//if(NODE.op=='R'){
            for(int i=1;i<=n;i++){
                if(hook[i]==0){//如果hook数组里该位置是空的,则还钥匙
                    hook[i]=nd.num;
                    break;
                }
            }
        }
        /*printf("status: ");
        for(int i=1; i<=n; i++)//输出中间每次借和还的中间结果
            printf("%d ", hook[i]);
        printf("\n");*/
    }
    for(int i=1; i<=n; i++) {
        printf("%d ", hook[i]);
    }
    printf("\n");
    return 0;
}

等值子串

【问题描述】如果字符串的一个子串(其长度大于1)的各个字符均相同,则称之为等值子串。试设计一算法,求出串S中一个长度最大的等值子串;如果串S 中不存在等值子串,则输出信息no

【输入形式】输入一个字符串,并以!结束

【输出形式】输出第一个出现的最长字符串,如果没有输出no

【样例输入】aabc123abc123cc!

【样例输出】aa

【样例输入】abceebccadddddaaadd!

【样例输出】ddddd

#include<iostream>
using namespace std;
#define N 1000
void input(char s[N])
{
    int i = 0;
    for(i = 0; ; i ++)
    {
        cin>>s[i];
        if(s[i] == '!') break;
    }
}
int main()
{
    char s[N];
    input(s);
    char res;
    int lenmax = 0;
    int i = 0;
    int j = 1;
    for(i = 0; s[j] != '!'; i ++)
    {
        if(s[i] != s[j])
        {
            j ++;
            continue;
        }
        if(s[i] == s[j])
        {
            int count = 1;
            while(s[i] == s[j])
            {
                count ++;
                if(s[j] == '!')
                {
                    break;
                }
                j ++;
            }
            if(count > lenmax)
            {
                res = s[j - 1];
                lenmax = count;
            }
            i = j - 1;
            if(s[j] == '!') break;
            j ++;
            continue;
        }
    }
    if(lenmax == 0)
    {
        cout<<"no";
    }
    while(lenmax --)
    {
        cout<<res;
    }
    return 0;
}

KMP模式匹配

【问题描述】KMP算法是字符串模式匹配算法中较为高效的算法之一,其在某次子串匹配母串失败时并未回溯母串的指针而是将子串的指针移动到相应的位置。

【输入形式】3组字符串,每组字符串占一行。每行包含由空格分隔的两个字符串,字符串仅由英文小写字母组成且长度不大于100。

【输出形式】每组数据输出1行,输出后一个字符串在前一个字符串中的位置,如果不匹配,则输出0。

【样例输入】

string str
thisisalongstring isa
nosubstring subt

【样例输出】

1

5

0

【提示】表示字符串的数据结构可以是字符数组或用串类实现。KMP算法调用很简单,但难的是理解算法的思想。掌握算法的思想才能说是掌握算法。

#include<iostream>
using namespace std;
int main()
{
    int num = 3;
    cycle1:while(num --)
    {
        string s;
        getline(cin,s);
        int lens = s.length(); //第二个串终止位置
        int i = 0;
        for(i = 0; s[i] != ' '; i ++){}
        int firlen = i; //第一个串终止位置
        int j = i + 1; //第二个串起始位置
        //cout<<firlen<<" "<<lens<<endl;
        int count = 0;
        for(i = 0; i < firlen; i ++)
        {
            while(s[i] != s[j])
            {
                i ++;
            }
            while(j < lens && i < firlen && s[i] == s[j] )
            {
                count ++;
                i ++;
                j ++;
            }
            if(j == lens)
            {
               // cout<<i <<" "<<count<<endl;
                cout<<i - count + 1 <<endl;
                goto cycle1;
            }
            else
            {
                count = 0;
                if(i ==  firlen)
                {
                    cout<<0<<endl;
                    goto cycle1;
                }
                else if(s[i] != s[j])
                {
                    j = firlen + 1;
                    i --;
                    continue;
                }
            }
        }
        if(count == 0)
        {
            cout<<0<<endl;
        }
    }
    return 0;
}

大整数相乘

【问题描述】输出两个不超过100位的大整数的乘积。

要求用串这一章的“块链串”知识实现两个大整数的相乘。比如每一个结点存储5位,12345678983426801则需要4个结点;先实现大整数乘上一个一位数;再实现两个大数相加。

用这样的链式存储形式便于计算:12—>34567—>89834—>26801

或者:26801—>89834—>34567—>12

【输入形式】

两个大数

【输出形式】

相乘的结果

【样例输入】

1234567891011121314151617181920

2019181716151413121110987654321

【样例输出】

2492816912877266687794240983772975935013386905490061131076320

#include<bits/stdc++.h>
using namespace std;
int result[210];
struct Link
{
    int maxsize;
    int num[5];
    Link *next;
};

//初始化块链串
Link *Init(Link *p,char s[])
{
    Link *head = p;
    for(int i=0; s[i]!='\0'; i++)
    {
        if(i!=0&&i%5==0)//用过一轮,数组满了,需要重新分配空间
        {
            p->next = new Link();
            p = p->next;
        }
        p->num[i%5]=s[i]-'0';
        p->maxsize = i%5;
    }
    p->next=NULL;
    return head;
}

//核心算法
void bigNum(Link *head1,Link *head2)
{
    Link *p,*q;

    //M,N是偏移值
    //M处理第一个串的不同节点之间的相加时的定位
    //N处理另一个串
    int N=0,M=0;
    int i=0,j;
    //max是当前最大长度。
    int max=0;

    //乘法部分:
    //从第一个串的头部开始遍历。
    for(p=head1; p!=NULL; p=p->next)
    {
        //如果第一个串一个结点存不下所有的值,那么就涉及到不同结点之间,相加乘积时的定位问题
        if(p!=head1)
            N+=5;
        //遍历第一个串的具体结点
        for(i=0; i<=p->maxsize; i++)
        {
            for(q=head2; q!=NULL; q=q->next)
            {
                if(q!=head2)
                    M+=5;
                for(j=0; j<=q->maxsize; j++)
                {
                    //因为涉及到进位问题,所以+1预留一个进位空间。
                    //M+N+i+j+1即是目前需要相加的偏移量。
                    result[M+N+i+j+1]+=q->num[j]*p->num[i];
                    //cout<<i<<" "<<j<<" "<<i+j+N+M+1<<endl;
                    //得到最大长度,便于确定result结果数组的长度。
                    max=max>M+N+1+j+i?max:M+N+i+j+1;
                }
            }
            //M和N的区别,M需要重新置为0
            M=0;
        }
    }

   /*for(int k=0; k<=max; k++)
        cout<<result[k]<<" ";
    cout<<endl;*/

    //处理进位:
    for(int k=max;k>0; k--)
    {
        if(result[k]>=10)
        {
            result[k-1]+=result[k]/10;
            result[k]%=10;
        }
    }

    //如果结果没有多一位,第一位则是0,输出时就要跳过
    //如果进位,就直接输出。
    int k=0;
    if(result[k]==0)
        k++;
    for(; k<=max; k++)
        cout<<result[k];
    cout<<endl;
}
int main()
{
    char s[100],t[100];
    cin>>s>>t;
    Link *head1,*head2,*p;
    p = new Link();
    head1 = Init(p,s);
    p = new Link();
    head2 = Init(p,t);
    bigNum(head1,head2);
    return 0;
}

最长公共子串

【问题描述】

给定两个字符串,求出它们两个最长的相同子字符串的长度。

最长公共子串(Longest Common Substring)是一个非常经典的面试题目,在实际的程序中也有很高的实用价值。同学们不应该单单只是写出该问题的基本解决代码而已,关键还是享受把算法一步步的优化,让时间和空间复杂度一步步的减少的惊喜。

【输入形式】

两个字符串

【输出形式】

最长的公共子串,若没有则输出“no”

【样例输入】

acbcbcef

abcbced

【样例输出】

bcbce

【温馨提示】

问题的关键不在于提交代码并通过测试数据,而是你是否真正掌握了所有求解的算法。

#include<iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;

char* GetCommonString(char *str1, char *str2) {
	if (str1 == NULL || str2 == NULL) {
		return NULL;
	}

	char *longstr, *shortstr, *substr;

	//找出两个字符串的长串和短串
	if (strlen(str1) < strlen(str2)) {
		longstr = str2;
		shortstr = str1;
	}
	else {
		longstr = str1;
		shortstr = str2;
	}

	//如果短串刚好为长串的子串,直接返回
	if (strstr(longstr, shortstr) != NULL) {
		return shortstr;
	}

	//寻找两个字符串中的最大公共子串
	substr = (char*)malloc(strlen(shortstr));
	//控制子串个数
	int i = 0;
	for (i = strlen(shortstr) - 1; i > 0; i--) {
		//控制子串位置
		int j = 0;
		for (j = 0; j <= strlen(shortstr) - i; j++) {
			strncpy(substr, &shortstr[j], i);
			substr[i] = '\0';
			if (strstr(longstr, substr) != NULL) {
				return substr;
			}
		}
	}

	if (substr != NULL) {
		free(substr);
		substr = NULL;
	}

	return NULL;
}

int main() {
	char str1[50];
	char str2[50];
	cin >> str1;
	cin >> str2;
	cout << GetCommonString(str1, str2) << endl;
	return 0;
}

判断两个字符串是否匹配

【问题描述】判断两个字符串是否匹配,其中一个字符串中包括通配符或?(串)。代表0个或多个字符,?代表一个字符
【输入形式】分两行入两个字符串,以#结束,其中一个字符串中包括通配符
或?(串),另一个为不包含
和?的确定字符串
【输出形式】判断两个字符串是否匹配,若匹配,输出yes,不匹配输出no
【样例输入】

da?atue#
datastructure#
【样例输出】
yes
【样例说明】 第一个字符串中包含通配符,第二个字符串为确定字符串。字符串中可能有空格,字母均为小写字母。
【评分标准】 请尽量使用效率高的算法,如结合KMP算法的思想。

提示:?可看做对任一字符的匹配,*可看做对给出的有效字符(串)的匹配。

#include<iostream>
#include<stdio.h>
#define M 1000
using namespace std;
bool Strcmp(char s1[],char s2[],int len1,int len2)
{
    int i=0,j=0,start1=-1,start2=-1;
    while(j<len2)
    {
        if(s1[i]==s2[j]||s1[i]=='?')
        {
            i++;j++;
        }///暂且让* 匹配空字符
        else if(i<len1 && s1[i]=='*')
        {
            start1=i;
            start2=j;
            i++;
        }
        else if(start1!=-1)
        {
            i=start1+1;
            j=++start2;
        }
        else
        {
            return false;
        }
    }
    while(i<len1 && s1[i]=='*')
        i++;
    return i==len1;
}
int Input(char c[])
{
    char ch;
    int i=0;
    scanf("%c",&ch);
    while(ch!='#')
    {
        c[i++]=ch;
        scanf("%c",&ch);
    }
    getchar();
    return i;
}
int main()
{
    char s1[M],s2[M];
    int len1,len2;
    len1=Input(s1);
    len2=Input(s2);
    bool flag = Strcmp(s1,s2,len1,len2);
    if(flag)
        cout<<"yes"<<endl;
    else
        cout<<"no"<<endl;
    return 0;

}

最长回文子串

【问题描述】给定一个字符串s,找出s里第一次出现的最长的回文子串。

【样例输入1】babad
【样例输出1】bab

【样例输入2】cbbd
【样例输出2】bb

#include<iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;
char* longestPalindrome(char* s)
   {
   	int len = strlen(s);
   	if (len <= 1) { return s; }
   	//定义bool类型的dp,只能为true或false
   	bool dp[1001][1001];
   	memset(dp, 0, sizeof(dp));
   	dp[0][0] = 1;
   	for (int i = 1; i < len; i++)
   	{
   		dp[i][i] = true;
   		//一定不要忽略,在下面k=2会用到
   		dp[i][i - 1] = true;
   	}
   	int left = 0;
   	int max = 0;
   	//k表示回文子串的长度
   	for (int k = 2; k <= len; k++)
   	{
   		//i表示回文子串的起始位置
   		for (int i = 0; i < len - k + 1; i++)
   		{
   			if (s[i] == s[k - 1 + i] && dp[i + 1][k + i - 2])
   			{
   				dp[i][k - 1 + i] = true;
   				if (max < k - 1)
   				{
   					max = k - 1;
   					left = i;
   				}
   			}
   		}
   	}

   	char *arr = (char *)malloc(sizeof(int) * (max * 2));
   	int i = 0;
   	for (; i <= max; i++)
   	{
   		arr[i] = s[left++];
   	}
   	arr[i] = '\0';
   	return arr;
}
int main()
{
    char s[1000];
    cin>>s;
    cout<<longestPalindrome(s);
    return 0;
}

年号字串

【问题描述】这是一道蓝桥杯的真题。小明用字母 A 对应数字 1,B 对应 2,以此类推,用 Z 对应 26。对于 27 以上的数字,小明用两位或更长位的字符串来对应,例如 AA 对应 27,AB 对 应 28,AZ 对应 52,LQ 对应 329。
  请问 2019 对应的字符串是什么?请你编程解决该问题。

【输入形式】数字
【输出形式】字母
【样例输入】2019
【样例输出】BYQ

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    string str;
    while(n)
    {
        if(n%26==0)
        {
            str+='Z';
            n = n/26-1;
        }
        else {
            str+=char(n%26+'A'-1);
            n/=26;
        }
    }
    reverse(str.begin(),str.end());
    cout<<str;
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只可爱的小猴子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值