PTA-数据结构字符串与数组|C语言版

本文精选了多种字符串处理算法,如KMP字符串匹配及其应用案例,并提供了对称矩阵判断、稀疏矩阵运算及最大子矩阵和问题的解决方案。

目录

1、 字符串模式匹配(KMP)

2、 【模板】KMP字符串匹配

3、 统计子串

4、 好中缀

5、 病毒变种

6、 判断对称矩阵

 7、 三元组顺序表表示的稀疏矩阵转置运算Ⅰ

8、 三元组顺序表表示的稀疏矩阵加法

9、 三元组顺序表表示的稀疏矩阵转置Ⅱ

10、 最大子矩阵和问题


1、 字符串模式匹配(KMP)

给定一个字符串 text 和一个模式串 pattern,求 pattern 在text 中的出现次数。text 和 pattern 中的字符均为英语大写字母或小写字母。text中不同位置出现的pattern 可重叠。

输入格式:

输入共两行,分别是字符串text 和模式串pattern。

输出格式:

输出一个整数,表示 pattern 在 text 中的出现次数。

输入样例1:

zyzyzyz
zyz

输出样例1:

3

输入样例2:

AABAACAADAABAABA
AABA

输出样例2:

3
#include<stdio.h>
#include<string.h>
char pattern[1000000],text[1000000];
int n=0,m=0;
int next[1000000];
void next_pattern()
{
    int i=0,j=-1;
    next[0]=-1;
    while(i<m)
    {
        if(j==-1||pattern[i]==pattern[j])
        {
            i++;
            j++;
            next[i]=j;
        }
        else j=next[j];
    }
}
int kmp()
{
    int count=0,i=0,j=0;
    while(i<n)
    {
        while(i<n&&j<m)
        {
            if(j==-1||pattern[j]==text[i])
            {
                i++;
                j++;
            }
            else j=next[j];
        }
        if(j==m)
        {
        
            count++;
            i--;
            j=next[j-1];
        }
    }
    return count;
}
int main()
{
    while((text[n]=getchar())!='\n')n++;
    while((pattern[m]=getchar())!='\n')
        m++;
    next_pattern();
    int count=kmp();
    printf("%d",count);
    return 0;
}

2、 【模板】KMP字符串匹配

给出两个字符串text和pattern,其中pattern为text的子串,求出pattern在text中所有出现的位置。

为了减少骗分的情况,接下来还要输出子串的前缀数组next。

输入格式:

第一行为一个字符串,即为text。

第二行为一个字符串,即为pattern。

输出格式:

若干行,每行包含一个整数,表示pattern在text中出现的位置。

接下来1行,包括length(pattern)个整数,表示前缀数组next[i]的值,数据间以一个空格分隔,行尾无多余空格。

输入样例:

ABABABC
ABA

输出样例:

1
3
0 0 1

样例说明:

#include<stdio.h>
#include<string.h>
int n=0,m=0,next[1000000];
char pattern[1000000],text[1000000];
void next_pattern()
{
    int i=0,j=-1;
    next[0]=-1;
    while(i<m)
    {
        if(j==-1||pattern[i]==pattern[j])
        {
            i++;
            j++;
            next[i]=j;
        }
        else j=next[j];
    }
}
void kmp()
{
    int i=0,j=0;
    while(i<n)
    {
        if(j==-1||text[i]==pattern[j])
        {
            i++;
            j++;
        }
        else j=next[j];
        if(j==m)
            printf("%d\n",i-m+1);
    }
}
int main()
{
    int i;
    while((text[n]=getchar())!='\n')
        n++;
    while((pattern[m]=getchar())!='\n')
        m++;
    next_pattern();
    kmp();
    for(i=1;i<m;i++)
        printf("%d ",next[i]);
    printf("%d",next[m]);
}

3、 统计子串

编写算法,统计子串t在主串s中出现的次数。

输入格式:

首先输入一个整数T,表示测试数据的组数,然后是T组测试数据。每组测试数据在第一行中输入主串s,在第二行中输入子串t,s和t中不包含空格。

输出格式:

对于每组测试,若子串t在主串s中出现,则输出t在s中的子串位置和出现总次数,否则输出“0 0”。引号不必输出。

输入样例:

2
abbbbcdebb
bb
abcde
bb

输出样例:

2 4
0 0
#include<stdio.h>
#include<string.h>
void next_pattern(int m,char pattern[],int next[])
{
    int i=0,j=-1;
    next[0]=-1;
    while(i<m)
    {
        if(j==-1||pattern[i]==pattern[j])
        {
            i++;
            j++;
            next[i]=j;
        }
        else j=next[j];
    }
}
int kmp(int n,int m,int next[],char pattern[],char text[])
{
    int count=0,i=0,j=0,p=0;
    while(i<n)
    {
        if(pattern[j]==text[i]||j==-1)
        {
            i++;
            j++;
        }
        else j=next[j];
        if(j==m)
        {
            count++;
            if(p==0)
                printf("%d ",i-m+1);
            p++;
        }
    }
    if(count==0)
        printf("0 ");
    return count;
}
int main()
{
    int T;
    scanf("%d",&T);
    getchar();//换行符
    while(T--)
    {
        int n=0,m=0,next[1000000],count;
        char pattern[1000000],text[1000000];
        while((text[n]=getchar())!='\n')
            n++;
        while((pattern[m]=getchar())!='\n')
            m++;
        next_pattern(m,pattern,next);
        count=kmp(n,m,next,pattern,text);
        printf("%d\n",count);
    }
}

4、 好中缀

我们称一个字符串S的子串T为好中缀,如果T是去除S中满足如下条件的两个子串p和q后剩余的字符串。

(1)p是S的前缀,q是S的后缀;

(2)p=q;

(3)p和q是满足条件(1)(2)的所有子串中的第二长者。

注意一个字符串不能称为自己的前缀或后缀。好中缀至少为空串,其长度大于等于0,不能为负数。

输入格式:

输入为一个字符串S,包含不超过100000个字母。

输出格式:

输出为一个整数,表示好中缀的长度。

输入样例1:

abcabcxxxabcabc

输出样例1:

9

输入样例2:

xacbacba

输出样例2:

8

输入样例3:

aaa

输出样例3:

1

#include<stdio.h>
#include<string.h>
int next[1000000],n;
char text[1000000];
int next_text()
{
    int i=0,j=-1;
    next[0]=-1;
    while(i<n)
    {
        if(j==-1||text[i]==text[j])
        {
            i++;
            j++;
            next[i]=j;
        }
        else j=next[j];
    }
    return next[next[n]];
}
int main()
{
    scanf("%s",text);
    n=strlen(text);
    int k=next_text();
    if(2*k<n&&k>=0)
        printf("%d",n-2*k);
    else if(k==-1)//k=-1时,说明前缀和后缀都不相等,好中缀长度为字符串长度
        printf("%d",n);
    else printf("0");
}

5、 病毒变种

病毒DNA可以表示成由一些字母组成的字符串序列,且病毒的DNA序列是环状的。例如,假设病毒的DNA序列为baa,则该病毒的DNA序列有三种变种:baa,aab,aba。试编写一程序,对给定的病毒DNA序列,输出该病毒所有可能的DNA序列(假设变种不会重复)。

输入格式:

输入第一行中给出1个整数i(1≤i≤11),表示待检测的病毒DNA。 输入i行串序列,每行一个字符串,代表病毒的DNA序列,病毒的DNA序列长度不超过500。

输出格式:

依次逐行输出每个病毒DNA所有变种,各变种之间用空格分隔。

输入样例1:

1
baa

输出样例1:

baa aab aba 

输入样例2:

2
abc
baac

输出样例2:

abc bca cab 
baac aacb acba cbaa 

#include<stdio.h>
#include<string.h>
int T;
char dna[501];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",dna);
        int t=strlen(dna);
        int k=t-1,i;
        //dna病毒为环状,一共有t种序列
        //先输出输入的dna病毒序列
        for(i=0;i<t-1;i++)
            printf("%c",dna[i]);
        printf("%c ",dna[t-1]);
        //再输出剩下的变异dna病毒序列
        while(k--)
        {
            //整个字符串向左移
            int p=dna[0];
            for(i=0;i<t-1;i++)
                dna[i]=dna[i+1];
            dna[t-1]=p;
            //输出变异的dna病毒序列
            for(i=0;i<t-1;i++)
                printf("%c",dna[i]);
            printf("%c ",dna[t-1]);
        }
        printf("\n");
    }
}

6、 判断对称矩阵

将矩阵的行列互换得到的新矩阵称为转置矩阵。

把m×n矩阵
A=⎣⎡​a11​a21​⋅⋅⋅am1​​a12​a22​⋅⋅⋅am2​​⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅​a1n​a2n​⋅⋅⋅amn​​⎦⎤​
的行列互换之后得到的矩阵,称为 A 的转置矩阵,记作 AT ,

AT=⎣⎡​a11​a12​⋅⋅⋅a1n​​a21​a22​⋅⋅⋅a2n​​⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅​am1​am2​⋅⋅⋅amn​​⎦⎤​

由定义可知, A 为m×n 矩阵,则 AT 为 n×m 矩阵。例如,
A=[1−2​01​23​]
,
AT=⎣⎡​102​−213​⎦⎤​.
n×n矩阵称之为 n阶方阵,

如果 n 阶方阵和它的转置相等,即 AT=A ,则称矩阵 A 为对称矩阵。

输入格式:

在第一行内给出n值(1<n<100)。

从第二行以后给出n阶矩阵所有行的元素值。

输出格式:

当输入的n阶矩阵是对称矩阵,输出“Yes”,否则输出“No”。

输入样例:

3
1 0 2
-2 1 3
4 3 2

输出样例:

No

输入样例:

3
1 -2 4
-2 1 3
4 3 2

输出样例:

Yes
#include<stdio.h>
int main()
{
    int n,a[101][101],i,j,flag=1;;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            scanf("%d",&a[i][j]);
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
        {
            if(a[i][j]!=a[j][i])
            {
                flag=0;
                break;
            }
        }
    if(flag==0)
        printf("No");
    else printf("Yes");
}

 7、 三元组顺序表表示的稀疏矩阵转置运算Ⅰ

三元组顺序表表示的稀疏矩阵转置。

输入格式:

输入第1行为矩阵行数m、列数n及非零元素个数t。
按行优先顺序依次输入t行,每行3个数,分别表示非零元素的行标、列标和值。

输出格式:

输出转置后的三元组顺序表结果,每行输出非零元素的行标、列标和值,行标、列标和值之间用空格分隔,共t行。

输入样例1:

3 4 3
0 1 -5
1 0 1
2 2 2

输出样例1:

0 1 1
1 0 -5
2 2 2

#include<stdio.h>
 typedef struct
 {
     int i,j,num;
 }Num;
struct node
{
    int m,n,t;
    Num data[100000];
}T,S;
int main()
{
    int i,j,x=0;
    scanf("%d%d%d",&T.m,&T.n,&T.t);
    for(i=0;i<T.t;i++)
        scanf("%d%d%d",&T.data[i].i,&T.data[i].j,&T.data[i].num);
    S.m=T.n;
    S.n=T.m;
    S.t=T.t;
    for(i=0;i<T.n;i++)
    {
        for(j=0;j<T.t;j++)
        {
            if(i==T.data[j].j)
            {
                S.data[x].i=T.data[j].j;
                S.data[x].j=T.data[j].i;
                S.data[x].num=T.data[j].num;
                x++;
            }
        }
    }
    for(i=0;i<x;i++)
        printf("%d %d %d\n",S.data[i].i,S.data[i].j,S.data[i].num);
}

8、 三元组顺序表表示的稀疏矩阵加法

三元组顺序表表示的稀疏矩阵加法。

输入格式:

输入第1行为两个同型矩阵的行数m、列数n,矩阵A的非零元素个数t1,矩阵B的非零元素个数t2。
按行优先顺序依次输入矩阵A三元组数据,共t1行,每行3个数,分别表示非零元素的行标、列标和值。
按行优先顺序依次输入矩阵B三元组数据,共t2行,每行3个数,分别表示非零元素的行标、列标和值。

输出格式:

输出第1行为相加后矩阵行数m、列数n及非零元素个数t。
输出t行相加后的三元组顺序表结果,每行输出非零元素的行标、列标和值,每行数据之间用空格分隔。

输入样例1:

4 4 3 4
0 1 -5
1 3 1
2 2 1
0 1 3
1 3 -1
3 0 5
3 3 7

输出样例1:

4 4 4
0 1 -2
2 2 1
3 0 5
3 3 7

#include<stdio.h> 
typedef struct
{
    int i,j,num;//存储每个元素的行标、列标、值
}Num;
struct node
{
    int n,m,t;//矩阵的行数、列数、非零元素个数
    Num data[100000];
}T1,T2,S;//T1和T2为输入的两个稀疏矩阵
int main()
{
    scanf("%d%d%d%d",&T1.m,&T1.n,&T1.t,&T2.t);
    int i=0,j;
    for(i=0;i<T1.t;i++)
    {
        scanf("%d%d%d",&T1.data[i].i,&T1.data[i].j,&T1.data[i].num);
    }
    for(i=0;i<T2.t;i++)
    {
        scanf("%d%d%d",&T2.data[i].i,&T2.data[i].j,&T2.data[i].num);
    }
    int x=0;
    i=0,j=0;
    while(i<T1.t&&j<T2.t)
    {
        if(T1.data[i].i==T2.data[j].i&&T1.data[i].j==T2.data[j].j)//当两个矩阵在同一位置都存在值时
        {
            S.data[x].i=T1.data[i].i;
            S.data[x].j=T1.data[i].j;
            S.data[x].num=T1.data[i].num+T2.data[j].num;
            x++;
            if(S.data[x-1].num==0)//如果在该点的和为0,则要去掉该点
                x--;
            i++;
            j++;
        }
        //两稀疏矩阵为行优先输入
        //若T1和T2在同一行且T2的列标小,或者不在同一行且T2的行标小,则T2值直接赋值给S
        else if((T1.data[i].i==T2.data[j].i&&T1.data[i].j>T2.data[j].j)||T1.data[i].i>T2.data[j].i)
            S.data[x++]=T2.data[j++];
        //若T1和T2在同一行且T1的列标小,或者不在同一行且T1的行标小,则T1值直接赋值给S
        else if((T1.data[i].i==T2.data[j].i&&T1.data[i].j<T2.data[j].j)||T1.data[i].i<T2.data[j].i)
            S.data[x++]=T1.data[i++];
    }
    while(i<T1.t)//只剩下T1矩阵时
    {
        S.data[x++]=T1.data[i++];
    }
    while(j<T2.t)//只剩下T1=2矩阵时
    {
        S.data[x++]=T2.data[j++];
    }
    printf("%d %d %d\n",T1.m,T1.n,x);
    for(i=0;i<x;i++)
        printf("%d %d %d\n",S.data[i].i,S.data[i].j,S.data[i].num);
}

9、 三元组顺序表表示的稀疏矩阵转置Ⅱ

三元组顺序表表示的稀疏矩阵转置Ⅱ。设a和b为三元组顺序表变量,分别表示矩阵M和T。要求按照a中三元组的次序进行转置,并将转置后的三元组置入b中恰当的位置。

输入格式:

输入第1行为矩阵行数m、列数n及非零元素个数t。
按行优先顺序依次输入t行,每行3个数,分别表示非零元素的行标、列标和值。

输出格式:

按置入b中的顺序输出置入的位置下标,转置后的三元组行标、列标和值,数据之间用空格分隔,共t行。

输入样例1:

3 4 3
0 1 -5
1 0 1
2 2 2

输出样例1:

1 1 0 -5
0 0 1 1
2 2 2 2

#include<stdio.h>
 typedef struct
 {
     int i,j,num,y;
 }Num;
struct node
{
    int m,n,t;
    Num data[100000];
}T,S;
int main()
{
    int i,j,x=0,y=0;
    scanf("%d%d%d",&T.m,&T.n,&T.t);
    for(i=0;i<T.t;i++)
        scanf("%d%d%d",&T.data[i].i,&T.data[i].j,&T.data[i].num);
    S.m=T.n;
    S.n=T.m;
    S.t=T.t;
    //进行稀疏矩阵的逆置
    for(i=0;i<T.n;i++)
    {
        for(j=0;j<T.t;j++)
        {
            if(i==T.data[j].j)
            {
                S.data[x].y=x;
                S.data[x].i=T.data[j].j;
                S.data[x].j=T.data[j].i;
                S.data[x].num=T.data[j].num;
                x++;
            }
        }
    }
    for(i=0;i<T.t;i++)
    {
        int k=T.data[i].num;//S按列优先顺序排序,T按行优先顺序排序
        //题目输出要求按行优先顺序排序
        for(j=0;j<T.t;j++)
        {
            if(S.data[j].num==k)
                printf("%d %d %d %d\n",S.data[j].y,S.data[j].i,S.data[j].j,S.data[j].num);
        }
    }
}

10、 最大子矩阵和问题

最大子矩阵和问题。给定m行n列的整数矩阵A,求矩阵A的一个子矩阵,使其元素之和最大。

输入格式:

第一行输入矩阵行数m和列数n(1≤m≤100,1≤n≤100),再依次输入m×n个整数。

输出格式:

输出第一行为最大子矩阵各元素之和,第二行为子矩阵在整个矩阵中行序号范围与列序号范围。

输入样例1:

5 6
60 3 -65 -92 32 -70
-41 14 -38 54 2 29
69 88 54 -77 -46 -49
97 -32 44 29 60 64
49 -48 -96 59 -52 25

输出样例1:

输出第一行321表示子矩阵各元素之和,输出第二行2 4 1 6表示子矩阵的行序号从2到4,列序号从1到6

321
2 4 1 6
#include<stdio.h>
#define N 1001//若范围小的话会出现段错误
int sum[N][N];
int main()
{
    int n,m,i,j,num;
    scanf("%d%d",&m,&n);
    for(i=1;i<=m;i++)
        sum[i][0]=0;
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
        {
            scanf("%d",&num);
            sum[i][j]=sum[i][j-1]+num;
        }
    int t=-1,k=0;
    int left,right,x1=0,x2=0,y1=0,y2=0;
    for(left=1;left<=n;left++)//遍历求和的起始列
        for(right=left;right<=n;right++)//遍历求和的终止列
        {
            k=0;
            for(i=1;i<=m;i++)//行数
            {
                if(k<0)
                    k=0;
                k=k+sum[i][right]-sum[i][left-1];
                if(k>t)
                {
                    t=k;
                    y1=left;
                    y2=right;
                    x2=i;
                }
                
            }
        }
    for(i=1;i<=x2;i++)//遍历求和的起始行
    {
        k=0;
        for(j=i;j<=x2;j++)//遍历求和的终止行
            k=k+sum[j][y2]-sum[j][y1-1];
        if(k==t)
        {
            x1=i;
            break;
        }
    }
    printf("%d\n%d %d %d %d",t,x1,x2,y1,y2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值