描述

给出三个队列 s1,s2,s3 ,判断 s3 是否是由 s1 和 s2 交叉得来。 如:s1 为 aabcc , s2 为 dbbca。 当 s3 为 aadbbcbcac 时,返回 true(即将 s1 拆成三部分: aa,bc,c 分别插入 s2 对应位置) 否则返回 false。

输入

aabcc,dbbca,aadbbcbcac

输出

true

输入样例

aabcc,dbbca,aadbbcbcac
aabcc,dbbca,aadbbbaccc
a,b,ab
a,b,ba
a,b,ac
abc,bca,bcaabc
abc,bca,aabbcc

输出样例

true
false
true
true
false
true
false

AC代码:

#include <bits/stdc++.h>
#include<list>
#include<vector>
using namespace std;

bool crossQueue(string s)
{
    int a = 0,b = 0,c = 0;
    int cnt = 0;
    a = 0;
    cnt = 0;
    for(int i = 0; i<s.length(); i++)
    {
        if(s[i] == ',' )
        {
            if(cnt == 0)
            {
                b = i;
                cnt++;
            }
            else
            {
                c= i;
            }
        }
    }
    string arr1 = s.substr(a,b);
    string arr2 = s.substr(b+1,c-b-1);
    string arr3 = s.substr(c+1,s.length()-c-1);
    int len1 = arr1.length();
    int len2 = arr2.length();
    int len3 = arr3.length();
    if(len3 != len1 + len2)
    {
        return false;
    }
    if(len1 == 0)
    {
        return (arr2 == arr3);
    }
    if(len2 == 0)
    {
        return (arr1 == arr3);
    }
    int dp[len1+1][len2+1];
    memset(dp,0,sizeof(dp));
    dp[0][0] = 1;
    for (int i = 1; i <= len1; i++)
    {
        if (arr1[i-1] == arr3[i-1])
            dp[i][0] = dp[i - 1][0];
    }
    for (int i = 1; i <= len2; i++)
    {
        if (arr2[i-1] == arr3[i-1])
            dp[0][i] = dp[0][i - 1];
    }

    for (int i = 1; i < len1 + 1; i++)
    {
        for (int j = 1; j < len2 + 1; j++)
        {
            int t = i + j;
            if (arr1[i-1] == arr3[t-1])
            {
                dp[i][j] = dp[i - 1][j] || dp[i][    j];
            }
            if (arr2[j-1] == arr3[t-1])
            {
                dp[i][j] = dp[i][j - 1] || dp[i][j];
            }
        }
    }
    /* for(int i = 0;i<len1+1;i++){
             for(int j = 0;j<len2+1;j++){
                 cout<<dp[i][j]<<" ";
             }
             cout<<endl;

     }*/
    if (dp[len1][len2] == 1)
    {
        return true;
    }
    else return false;
}

int main()
{
    string s;

    while(cin>>s)
    {
        if(crossQueue(s))
        {
            cout<<"true"<<endl;
        }
        else cout<<"false"<<endl;

    }
}

 

描述

给出一个有序数列随机旋转之后的数列,如原有序数列为:[0,1,2,4,5,6,7] ,旋转之后为[4,5,6,7,0,1,2]。 假定数列中无重复元素,且数列长度为奇数。 求出旋转数列的中间值。如数列[4,5,6,7,0,1,2]的中间值为4。

输入

4,5,6,7,0,1,2

输出

4

输入样例

1
1,2,3
4,5,6,7,0,1,2
12,13,14,5,6,7,8,9,10

输出样例

1
2
4
9

AC代码:

#include <bits/stdc++.h>
#include<vector>
using namespace std;

int main()
{
    string s;

    while(cin>>s)
    {
        s = s+",";
        bool flag = false;
        vector<int>a(0);
        int b = 0;
        int j = 0;
        int k = 0;
        int cnt = 0;
        int temp = -1;
        for(int i = 0; i<s.length(); i++)
        {
            flag = false;
            b = 0;
            k = j;
            if(s[i] ==',')
            {
                while(k != i)
                {
                    b = b*10+(s[k]-48);
                    k++;
                }
                flag = true;
                // cout<<b<<endl;
                j = i+1;
            }
            if(flag == true)
            {
                a.push_back(b);
            }
        }
        temp = -1;
        for(int i = 0;i<a.size()-1;i++){
            if(a[i] > a[i+1]){
                temp = i;
            }
        }
        if(temp == -1){
            cout<<a[a.size()/2]<<endl;
        }
        else if(temp >= a.size()/2){
            cout<<a[a.size()/2-temp]<<endl;
        }
        else if(temp < a.size()/2){
            cout<<a[a.size()/2+temp+1]<<endl;
        }
    }
}

 

描述

输入一个乱序的连续数列,输出其中最长连续数列长度,要求算法复杂度为 O(n) 。

输入

54,55,300,12,56

输出

3

输入样例

100,4,200,1,3,2
54,55,300,12
1
5,4,3,2,1
1,2,3,4,5,6

输出样例

4
2
1
5
6

AC代码:

#include <bits/stdc++.h>
#include<list>
#include<vector>
using namespace std;

int main()
{
    string s;

    while(cin>>s)
    {
        bool flag = false;
        list<int>a(0);
    int b = 0;
        int j = 0;
        int k = 0;
        s = s +",";
        for(int i = 0;i<s.length();i++)
        {
            flag = false;
            b = 0;
              k = j;
            if(s[i] ==',')
            {
                while(k != i){
                    b = b*10+(s[k]-48);
                      k++;
                }
                flag = true;
               // cout<<b<<endl;
                j = i+1;
            }
            if(a.size() == 0 && flag == true)
            {
                a.push_back(b);
                //cout<<"1:"<<b<<endl;
            }
            else if(flag == true)
            {
                for( list<int>::iterator it=a.begin(); ; it++)
                {
                    list<int>::iterator it1=a.end();
                    --it1;
                    if(*it > b)
                    {
                        a.insert(it,b);
                       // cout<<"2:"<<b<<endl;
                        break;
                    }
                    if(it == it1)
                    {
                        a.push_back(b);
                       // cout<<"3:"<<b<<endl;
                        break;
                    }
                }
            }
        }

        if(a.size() == 0)
        {
            cout<<"0"<<endl;
        }
        else if(a.size() == 1)
        {
            cout<<"1"<<endl;
        }
        else
        {
            int cnt = 1;
            int maxCnt = 0;
            list<int>::iterator it1=a.begin();
            it1++;
            for( list<int>::iterator it=a.begin(); it1!=a.end(); it++,it1++)
            {
                if(*it+1 == *it1)
                {
                    cnt++;
                    if(cnt > maxCnt)
                    {
                        maxCnt = cnt;
                    }
                }
                else
                {
                    cnt = 1;
                }
            }
            cout<<maxCnt<<endl;
        }
    }
}

 

好设计是简单的设计。
好设计是解决主要问题的设计。
好的设计并非一定要有趣,但是很难想象完全无趣的设计会是好设计。——Paul Graham《黑客与画家》

描述

两个长度超出常规整形变量上限的大数相减,请避免使用各语言内置大数处理库,如 Java.math.BigInteger 等。

输入

有 N 行测试数据,每一行有两个代表整数的字符串 a 和 b,长度超过百位。规定 a>=b,a, b > 0。 测试结果可以用 linux 小工具 bc进行测试是否正确。

输出

返回表示结果整数的字符串。

输入样例

1231231237812739878951331231231237812739878951331231231237812739878951331231231237812739878951331231231237812739878951331231231237812739870-89513312312312378127398789513312312312378127398789513312312312378127398789513
1231231237812739878951331231231237812739878951331231231237812739878951331230000000000000000000000001-331231231237812739878951331231231

输出样例

1231231237812739878951331231231237812739878951331231231237812650365639018918853110413950365639018918853110413950365639018918853110413950357
1231231237812739878951331231231237812739878951331231231237812739878620099998762187260121048668768770

AC代码:

#include <bits/stdc++.h>
using namespace std;

string ASubB(string a,string b){//默认A比B大
     int i = a.size();
     int j = b.size();
     string c;
     for(int k = 0;k<i-j;k++){
        c = c+"0";
     }
     c = c+b;
     //转化为位数相同的A-C的问题
     for(int k = c.size();k>=0;k--){
        if(a[k] == -1){
            a[k] = 0;
            a[k-1] = a[k-1] - 1;
        }
        if(a[k] < c[k]){
            a[k-1]--;
            a[k] = a[k] +10;
        }
        a[k] = a[k] - c[k]+48;
     }
     return a;
}


int main()
{
    string s;
    while(cin>>s){
      int k = s.find("-");
      //cout<<k<<endl;
      string a = s.substr(0,k);
      string b = s.substr(k+1,s.size());
      //cout<<a<<endl;
      //cout<<b<<endl;
cout<<ASubB(a,b)<<endl;
    }
}

 

It’s not a bug, it’s a feature.——Null

描述

给出N个数字。其中仅有一个数字出现过一次,其他数字均出现过两次,找出这个出现且只出现过一次的数字。要求时间和空间复杂度最小。

输入

输入多个数字,每个数字以空格分开。数字数量 N < 20,输入数字的最大值小于 256.

输出

输出内容为只出现过唯一一次的数字

输入样例

10 10 11 12 12 11 16

输出样例

16

AC代码:

#include <bits/stdc++.h>
#include<iostream>
#include<map>
using namespace std;
int main()
{

    map<int,int>a;
    int b;
    while(cin>>b)
    {
        a[b]++;
        if (cin.get() == '\n')
            break;
    }
    for(map<int,int>::iterator iter=a.begin();iter!=a.end();iter++){
          if(iter->second == 1){
            cout<<iter->first<<endl;
          }
	}
}

 

在 C 语言中,我们不能使用 goto 语句来跳转到另一个函数中,但提供了两个函数——setjmp 和 longjmp来完成这种类型的分支跳转。

我们都知道要想在一个函数内进行跳转,可以使用 goto 语句(不知怎么该语句在中国学生眼中就是臭名昭著,几乎所有国内教材都一刀切地教大家尽量不要使用它,但在我看来,这根本不是语言的问题,而是使用该语言的人,看看 Linux 内核中遍地是 goto 语句的应用吧!),但如果从一个函数内跳转到另一个函数的某处,goto 是不能完成的,那该如何实现呢?

函数原型

#include <setjmp.h>
int setjmp(jmp_buf env);

setjmp 函数的功能是将函数在此处的上下文保存在 jmp_buf 结构体中,以供 longjmp 从此结构体中恢复。

  • 参数 env 即为保存上下文的 jmp_buf 结构体变量;
  • 如果直接调用该函数,返回值为 0; 若该函数从 longjmp 调用返回,返回值为非零,由 longjmp 函数提供。根据函数的返回值,我们就可以知道 setjmp 函数调用是第一次直接调用,还是由其它地方跳转过来的。
void longjmp(jmp_buf env, int val);

longjmp 函数的功能是从 jmp_buf 结构体中恢复由 setjmp 函数保存的上下文,该函数不返回,而是从 setjmp 函数中返回。

  • 参数 env 是由 setjmp 函数保存过的上下文。
  • 参数 val 表示从 longjmp 函数传递给 setjmp 函数的返回值,如果 val 值为0, setjmp 将会返回1,否则返回 val。
  • longjmp 不直接返回,而是从 setjmp 函数中返回,longjmp 执行完之后,程序就像刚从 setjmp 函数返回一样。
#include <stdio.h>    
#include <setjmp.h>    
#include <windows.h>  
   
jmp_buf jmpbuffer;  
int i = 0;  
void test_jmp()  
{  
    ++i;  
    longjmp(jmpbuffer, i);  //跳转到setjmp处  
}  
   
int main(int argc, char **argv)  
{  
    int  ret = 0;  
    if ((ret = setjmp(jmpbuffer)) != 0) //类似于goto所用的tag,告诉longjmp应该返回到哪里    
    {  
        printf("jmp:%d\n", ret);  
        Sleep(200);  
    }  
    test_jmp();  
    return 0;  
}

 

以提取蝙蝠魔的攻击状态下的图片为例子。

首先使用WZCompare或者WZCompareR2等工具打开MapleStory的WZ文件。

Wz文件是MapleStory的基础文件,使用wizet公司为扩展名来加密、加压游戏资源,以减少游戏资源所占用的空间以及防止有人对wz文件作非法的修改,简单的说冒险岛这款游戏所有的图片、音乐、脚本等资源都包含在Wz文件中。

一个常规的MapleStory客户端内包括以下几个wz文件:

wz文件名
内容介绍
Base.wz
wz文件全体列表,不包括任何资源文件
Character.wz
游戏角色形象资源,包括人物发型、衣服、鞋子、武器等,另外坐骑、称号都在该文件内
Effect.wz
一些游戏效果,例如一些职业的剧情大图、人物身边的某些特效图
Etc.wz
这里包含的资源较少,列表数据较多
Item.wz
各种物品图标资源、椅子资源等
List.wz
暂不明用处的列表wz,后期已被删除
Map.wz
游戏地图资源,包括背景、前景、路面、摆设物、世界地图等
Mob.wz
怪物资源图
Morph.wz
角色变身形象资源图
Npc.wz
NPC的资源图
Quest.wz
游戏任务相关文字内容
Reactor.wz
敲击物(反应器)的资源图,例如一些需要普通攻击敲打的木箱等
Skill.wz
包含各职业技能图、怪物技能图等
Sound.wz
声音资源,包含各个地图的背景音乐、技能音效、界面音效等
String.wz
包含一些物品、地图、怪物、NPC名称等文字资源
TamingMob.wz
原用于归类坐骑图片资源,后来的坐骑图片资源都归类至Character.wz文件了
UI.wz
界面相关资源,包括游戏界面、职业创建界面、名片对话框、商城界面、鼠标指针等

在这里我们要提取的是怪物的资源图,所以应该去Mob.wz中寻找。

在这之前,我们先去String.wz中寻找到Mob.img.xml,从而能够找到怪物和对应结点的关系。

在这里我们发现蝙蝠魔的ID是9500171。

于是我们就可以去Mob.wz中寻找9500171.img啦。

如图所示,在9500171.img中还分有attack1,attack2,die1,fly,hit1等不同的分支,代表怪物在不同状态下的图。

MapleStory的怪物看起来都是动起来的,实际上是由图片一帧一帧连接起来的,需要gif的话把这些图按顺序拼起来就好啦。

 

按位运算

C语言有这些按位运算的运算符

  • &   按位的与
  • |    按位的或
  • ~  按位取反
  • ^   按位的异或
  • << 左移
  • >> 右移

按位与&

  • 如果x的第i位是1且y的第i位是1,那么(x&y)的第i位是1,否则的话(x&y)的第i位是0
  • 按位与常用于两种应用:
  • 让某一位或某些位为0
  • 取一个数中的一段

按位或|

  • 如果x的第i位是1或y的第i位是1,那么(x|y)的第i位是1,否则的话(x|y)的第i位是0
  • 按位或常用于两种应用:
  • 让某一位或某些位为1
  • 把两个数拼起来

按位取反~

  • 把1位变0,0位变1
  • 想要得到全部位为1的数:~0

逻辑运算VS按位运算

  • 对于逻辑运算,它只看到两个值:0和1
  • 可以认为逻辑运算相当于把所有非0值都变成1,然后做按位运算

按位异或

  • 如果x的第i位和y的第i位相等,那么(x^y)的第i位是0,否则的话(x^y)的第i位是1
  • 如果两个位相等,那么结果为0;不相等,结果为1
  • 对一个变量用同一个值异或两次,等于什么也没做

曾经面试金山WPS的时候有一道题问到:不使用额外的空间,交换两个整形数字。

有两种方法

方法一:算术方法

x = x + y;
y = x - y;
x = x - y;

方法二:异或方法

x = x^y;// 只能对int,char..
y = x^y;
x = x^y;

移位运算:左移<<

  • i << j
  • i中所有的位向左移动j个位置,而右边填入0
  • 所有小于int的类型,移位以int的方式来做,结果是int
  • x = x<<1 等价于 x = x*2
  • x = x<<n 在范围内等价于x = x*(2的n次方)

移位运算:右移>>

  • i >> j
  • i中所有的位向右移j位
  • 所有小于int的类型,移位以int的方式来做,结果是int
  • 对于unsigned的类型,左边填入0
  • 对于signed的类型,左边填入原来的最高位
  • x = x>>1 等价于 x = x/2
  • x = x>>n 等价于 x = x/(2的n次方)

no zuo no die

  • 移位的位数不要用负数,这是没有定义的行为

利用按位与和移位操作实现输出一个数的二进制:

#include<stdio.h>

int main(int argc,int *argv[])
{
    int number;
    scanf("%d",&number);
    unsigned int mask = 1;
    mask = mask << 31;
    while(mask)
    {
        if(number & mask)
        {
            printf("1");
        }
        else
        {
            printf("0");
        }
        mask = mask>>1;
    }
    return 0;
}

 

位段

把一个int的若干位组合成一个结构

如:

struct U0
{
    unsigned int leading : 3;
    unsigned int FLAG1:1;
    unsigned int FLAG2:1;
    int trailing :27;
};

 

  • 这样就可以直接用位段的成员名称来访问,以移位、与、或更方便
  • 编译器会安排其中的位的排列,不具有可移植性
  • 当所需的位超过一个int时会采用多个int

 

文件输入输出

  • 用>和<做重定向
  • 使用FILE

FILE

  • FILE *fopen(const char * restrict path,const char * restrict mode);
  • int fclose(FILE *stream);
  • fscanf(FILE* , …)
  • fprintf(FILE* , …)

打开文件的标准代码

FILE *fp = fopen("file","r");//文件名,只读模式
if(fp){
   fscanf(fp,...);
   fclose(fp);
  }else{
   ...
}

 

例子:(打开当前.c源代码目录下的1.txt文件中的数字并输出到终端。)

#include<stdio.h>

int main(int argc,int *argv[])
{
    FILE *fp = fopen("1.txt","r");
    if(fp)
    {
        int num;
        fscanf(fp,"%d",&num);
        printf("%d\n",num);
        fclose(fp);
    }
    else
    {
        printf("无法打开文件\n");
    }
    return 0;
}

 

fopen

第一个字符串参数为文件名,第二个字符串参数为模式

  • r   打开只读
  • r+   打开读写,从文件头开始
  • w   打开只写。如果不存在则新建,如果存在则清空
  • w+   打开读写。如果不存在则新建,如果存在则清空
  • a   打开追加。如果不存在则新建,如果存在则从文件尾开始
  • 在上述后面可以加x,代表只新建,如果文件已存在则不能打开

二进制文件

  • 其实所有的文件最终都是二进制的
  • 文本文件无非是用最简单的方式可以读写的文件
  • 而二进制文件是需要专门的程序来读写的文件
  • 文本文件的输入输出是格式化,可能经过转码

文本文件VS二进制文件

  • 文本的优势是方便人类读写,而且跨平台
  • 文本的缺点是程序输入输出需要经过格式化,开销大
  • 二进制的缺点是人类读写困难,可能因为int的大小不一致,大小端等问题导致不跨平台
  • 二进制的优点是程序读写快

程序为什么要文件

  • 配置:Unix用文本,Windows用注册表
  • 数据:稍微有点量的数据都放数据库了
  • 媒体:通过二进制,现实是程序通过第三方库来读写文件,很少直接读写二进制文件了

printf()

格式:%[flags][width][.prec][hlL]type

flag

  • –   左对齐
  • +   在前面放+或者-
  • (space) 正数留空
  • 0   0填充

width和pres

  • number   最小字符数
  • *   下一个参数是字符数
  • .number   小数点后面的位数
  • .*   下一个参数是小数点后的位数

hlL

  • hh   单个字节
  • h   short
  • l   long
  • ll   long long
  • L   long double

type

  • i或d   int
  • u   unsigned int
  • o   八进制
  • x   十六进制
  • X   大写十六进制
  • f或F   float
  • e或E   指数
  • g或G   float
  • a或A   十六进制浮点
  • c   char
  • s   字符串
  • p   指针
  • n   读入/写出的个数

scanf()

格式:%[flag]type

flag

  • *   跳过
  • 数字   最大字符数
  • hh   char
  • h   short
  • l   long,double
  • ll   long long
  • L   long double

type

  • d   int
  • i   整数,可能为十六进制或者八进制
  • u   unsigned int
  • o   八进制
  • x   十六进制
  • a,e,f,g   float
  • c   char
  • s   字符串
  • […]   所允许的字符
  • p   指针

printf()和scanf()的返回值

  • 读入的项目数
  • 输出的字符数
  • 在要求严格的程序中,应该判断每次调用scanf()或printf()的返回值,从而了解程序运行中是否存在问题