zoukankan      html  css  js  c++  java
  • 数据结构 | 串的块链存储表示法

    ————————————————————————————————————————————

    • 串的块链存储表示法

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    相关要点:

    • 通过链表方式存储时给每一个结点的字符数组分配固定的大小(块),大小可以根据需要手动调节,#define CHUNKSIZE 4 //块大小。
    • 由于串长不一定是结点大小的整倍数,所以链表的最后一个结点不一定被字符占满,未满的位置使用#符号补全
    • 定义头指针和尾指针指向链表,并给出一个变量存储链表的长度。设立尾指针的目的是为了便于进行联结操作。
    • 在链式存储方式中,结点大小的选择影响着串处理的效率。注意串值的存储密度,存储密度 = 串值所占的存储位/实际分配的存储位。
    • 串值的链式存储结构对联结操作有方便之处,但总体上不如另两种存储结构灵活,占用存储量大且操作复杂。

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    存储结构:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    实现代码:

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #define OK 1
      5 #define ERROR 0
      6 #define TRUE 1
      7 #define FALSE 0
      8 #define OVERFLOW -2
      9 typedef int Status;
     10 /* 存储结构 */
     11 #define blank '#'
     12 #define CHUNKSIZE 4 //块大小
     13 typedef struct Chunk
     14 {
     15     char ch[CHUNKSIZE];
     16     struct Chunk *next;
     17 } Chunk;
     18 typedef struct
     19 {
     20     Chunk *head, *rear; //串的头和尾指针
     21     int curlen; //串的当前长度
     22 } LString;
     23 /* 函数列表 */
     24 void InitString(LString *T);
     25 Status StrAssign(LString *T, char *chars);
     26 Status StrCopy(LString *T, LString S);
     27 Status StrEmpty(LString S);
     28 int StrCompare(LString S, LString T);
     29 int StrLength(LString S);
     30 Status ClearString(LString *S);
     31 Status Concat(LString *T, LString S1, LString S2);
     32 Status SubString(LString *Sub, LString S, int pos, int len);
     33 int Index(LString S, LString T, int pos);
     34 void Zip(LString *S); //压缩串
     35 Status StrInsert(LString *S, int pos, LString T);
     36 Status StrDelete(LString *S, int pos, int len);
     37 Status Replace(LString *S, LString T, LString V);
     38 void StrPrint(LString T);
     39 void DestroyString();
     40 /* 主函数 */
     41 int main()
     42 {
     43     int pos, len, flag;
     44     char *s1 = "Hello,LString!", *s2 = "Booooo!",  *s3 = "123#4", *s4 = "Hello,LString!!", *s5 = "Insert!", *s6 = "Insert!!", *s7 = "***", *s8 = "o"; //此种赋值方式,最后一个字符结束后,下一个字符位为空,可以通过*s1==NULL判断字符串结束
     45     LString t1, t2, t3, t4, t5, t6;
     46     InitString(&t1); //初始化t1
     47     InitString(&t2); //初始化t2
     48     printf("--------------------------
    ");
     49     printf("StrEmpty...OK.
    ");
     50     if(StrEmpty(t1))
     51         printf("t1 is Empty
    ");
     52     else
     53         printf("t1 is not Empty
    ");
     54     printf("StrLength...OK.
    ");
     55     printf("length:%d
    ", StrLength(t1));
     56     printf("--------------------------
    ");
     57     printf("StrAssign...OK.
    ");
     58     StrAssign(&t1, s1);
     59     printf("t1:");
     60     StrPrint(t1);
     61     printf("length:%d
    ", StrLength(t1));
     62     StrAssign(&t2, s2);
     63     printf("t2:");
     64     StrPrint(t2);
     65     printf("length:%d
    ", StrLength(t2));
     66     printf("--------------------------
    ");
     67     printf("StrCopy...OK.
    ");
     68     StrCopy(&t3, t1);
     69     printf("t3:");
     70     StrPrint(t3);
     71     printf("--------------------------
    ");
     72     InitString(&t4);
     73     StrAssign(&t4, s4);
     74     flag = StrCompare(t1, t4);
     75     printf("StrCompare...OK.
    ");
     76     StrPrint(t1);
     77     if (flag == 0)
     78         printf("==
    ");
     79     else if(flag > 0)
     80         printf(">
    ");
     81     else if(flag < 0)
     82         printf("<
    ");
     83     StrPrint(t4);
     84     printf("--------------------------
    ");
     85     printf("ClearString...OK.
    ");
     86     ClearString(&t3);
     87     if(StrEmpty(t3))
     88         printf("t3 is Empty
    ");
     89     else
     90         printf("t3 is not Empty
    ");
     91     printf("--------------------------
    ");
     92     printf("Concat...OK.
    ");
     93     InitString(&t5);
     94     Concat(&t5, t1, t2);
     95     printf("t5:");
     96     StrPrint(t5);
     97     printf("length:%d
    ", StrLength(t5));
     98     printf("--------------------------
    ");
     99     printf("StrInsert Insert! ...OK.
    ");
    100     StrAssign(&t3, s5);
    101     StrInsert(&t5, 21, t3);
    102     StrPrint(t5);
    103     printf("length:%d
    ", StrLength(t5));
    104     printf("--------------------------
    ");
    105     printf("StrDelete pos:13 len:5 ...OK.
    ");
    106     StrDelete(&t5, 13, 5);
    107     StrPrint(t5);
    108     printf("length:%d
    ", StrLength(t5));
    109     printf("--------------------------
    ");
    110     printf("SubString He ...OK.
    ");
    111     SubString(&t6, t5, 1, 2);
    112     printf("length:%d
    ", StrLength(t6));
    113     printf("--------------------------
    ");
    114     printf("Index Insert!! ...OK.
    ");
    115     StrPrint(t5);
    116     ClearString(&t3);
    117     StrAssign(&t3, s6);
    118     printf("index pos:%d
    ", Index(t5, t3, 1));
    119     printf("--------------------------
    ");
    120     printf("Replace o -> *** ...OK.
    ");
    121     ClearString(&t3);
    122     StrAssign(&t3, s7);
    123     ClearString(&t2);
    124     StrAssign(&t2, s8);
    125     Replace(&t5, t2, t3);
    126     StrPrint(t5);
    127     printf("--------------------------
    ");
    128     return OK;
    129 }
    130 /* 初始化空串,不分配空间 */
    131 void InitString(LString *T)
    132 {
    133     T->head = NULL;
    134     T->rear = NULL;
    135     T->curlen = 0;
    136 }
    137 Status StrAssign(LString *T, char *chars)
    138 {
    139     int len, blockNum, i, j;
    140     Chunk *p, *q;
    141     len = strlen(chars);
    142     if (!len || strchr(chars, blank)) //长度为0或包含#时结束
    143         return ERROR;
    144     T->curlen = len;
    145     blockNum = len / CHUNKSIZE; //计算结点数
    146     if (len % CHUNKSIZE)
    147         ++blockNum;
    148     for (i = 0; i < blockNum; ++i) //循环生成新节点
    149     {
    150         p = (Chunk *)malloc(sizeof(Chunk));
    151         if (!p)
    152             exit(OVERFLOW);
    153         if (T->head == NULL) //如果是第一个节点
    154             T->head = q = p;
    155         else
    156         {
    157             q->next = p;
    158             q = p;
    159         }
    160         for (j = 0; j < CHUNKSIZE && *chars; ++j) //每次新增一个块链即赋值,chars指针随之++,当chars指向空字符时结束
    161         {
    162             *(q->ch + j) = *chars;
    163             ++chars;
    164         }
    165         if (!*chars) //当*chars指向空字符(最后一个链块时)
    166         {
    167             T->rear = p;
    168             T->rear->next = NULL;
    169             for (; j < CHUNKSIZE; ++j) //当chars结束时j的值直接在此处使用
    170                 *(q->ch + j) = blank;
    171         }
    172     }
    173     return OK;
    174 }
    175 Status StrCopy(LString *T, LString S)
    176 {
    177     //另一个思路:将S中的内容读到char*中,调用StrAssign();
    178     Chunk *h = S.head, *p, *q;
    179     if (!h)
    180         return ERROR;
    181     T->head = (Chunk *)malloc(sizeof(Chunk)); //创建头节点
    182     p = T->head;
    183     *p = *h; //将S头节点的内容复制给T头节点
    184     h = h->next;
    185     while(h)
    186     {
    187         q = p;
    188         p = (Chunk *)malloc(sizeof(Chunk));
    189         q->next = p;
    190         *p = *h;
    191         h = h->next;
    192     }
    193     p->next = NULL;
    194     T->rear = p;
    195     return OK;
    196 }
    197 Status StrEmpty(LString S)
    198 {
    199     if (!S.curlen)
    200         return TRUE;
    201     else
    202         return FALSE;
    203 }
    204 int StrCompare(LString S, LString T)
    205 {
    206     int i = 0;
    207     Chunk *ps = S.head, *pt = T.head;
    208     while(ps && pt) //当有一个节点指向NULL时结束循环
    209     {
    210         for (i = 0; i < CHUNKSIZE; ++i) //节点内遍历
    211         {
    212             if (*(ps->ch + i) != *(pt->ch + i)) //如果指向的元素不同,则返回相减结果
    213             {
    214                 if (*(ps->ch + i) == blank)
    215                     return -1;
    216                 else if (*(pt->ch + i) == blank)
    217                     return 1;
    218                 return *(ps->ch + i) - *(pt->ch + i);
    219             }
    220         }
    221         ps = ps->next; //该节点对比结束,进入下一节点对比
    222         pt = pt->next;
    223     }
    224     return ps - pt; //当有一个指向NULL时,该指针为0,返回相减结果即可
    225 }
    226 // int StrCompare(LString S, LString T) //书上源码
    227 // {
    228 //     /* 若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0 */
    229 //     int i = 0; /* i为当前待比较字符在S,T串中的位置 */
    230 //     Chunk *ps = S.head, *pt = T.head; /* ps,pt分别指向S和T的待比较块 */
    231 //     int js = 0, jt = 0; /* js,jt分别指示S和T的待比较字符在块中的位序 */
    232 //     while(i < S.curlen && i < T.curlen)
    233 //     {
    234 //         i++; /* 分别找S和T的第i个字符 */
    235 //         while(*(ps->ch + js) == blank) /* 跳过填补空余的字符 */
    236 //         {
    237 //             js++;
    238 //             if(js == CHUNKSIZE)
    239 //             {
    240 //                 ps = ps->next;
    241 //                 js = 0;
    242 //             }
    243 //         }; /* *(ps->ch+js)为S的第i个有效字符 */
    244 //         while(*(pt->ch + jt) == blank) /* 跳过填补空余的字符 */
    245 //         {
    246 //             jt++;
    247 //             if(jt == CHUNKSIZE)
    248 //             {
    249 //                 pt = pt->next;
    250 //                 jt = 0;
    251 //             }
    252 //         }; /* *(pt->ch+jt)为T的第i个有效字符 */
    253 //         if(*(ps->ch + js) != *(pt->ch + jt))
    254 //             return *(ps->ch + js) - *(pt->ch + jt);
    255 //         else /* 继续比较下一个字符 */
    256 //         {
    257 //             js++;
    258 //             if(js == CHUNKSIZE)
    259 //             {
    260 //                 ps = ps->next;
    261 //                 js = 0;
    262 //             }
    263 //             jt++;
    264 //             if(jt == CHUNKSIZE)
    265 //             {
    266 //                 pt = pt->next;
    267 //                 jt = 0;
    268 //             }
    269 //         }
    270 //     }
    271 //     return S.curlen - T.curlen;
    272 // }
    273 int StrLength(LString S)
    274 {
    275     return S.curlen;
    276 }
    277 Status ClearString(LString *S)
    278 {
    279     Chunk *p, *q;
    280     if (!S->curlen)
    281         return ERROR;
    282     p = S->head;
    283     while(p)
    284     {
    285         q = p->next;
    286         free(p);
    287         p = q;
    288     }
    289     S->head = NULL;
    290     S->rear = NULL;
    291     S->curlen = 0;
    292     return OK;
    293 }
    294 Status Concat(LString *T, LString S1, LString S2)
    295 {
    296     LString T1, T2;
    297     InitString(&T1);
    298     InitString(&T2);
    299     StrCopy(&T1, S1);
    300     StrCopy(&T2, S2);
    301     T->curlen = S1.curlen + S2.curlen;
    302     T->head = T1.head;
    303     T1.rear->next = T2.head;
    304     T->rear = T2.rear;
    305     return OK;
    306 }
    307 void StrPrint(LString T)
    308 {
    309     Chunk *p;
    310     p = T.head;
    311     int i;
    312     while(p)
    313     {
    314         for (i = 0; i < CHUNKSIZE; ++i)
    315             if (*(p->ch + i) != blank)
    316                 printf("%c", *(p->ch + i));
    317         p = p->next;
    318     }
    319     printf("
    ");
    320 }
    321 void DestroyString() //无法销毁
    322 {
    323     ;
    324 }
    325 Status StrInsert(LString *S, int pos, LString T) //书上源码
    326 {
    327     /* 1≤pos≤StrLength(S)+1。在串S的第pos个字符之前插入串T */
    328     int i, j, k;
    329     Chunk *p, *q;
    330     LString t;
    331     if(pos < 1 || pos > StrLength(*S) + 1) /* pos超出范围 */
    332         return ERROR;
    333     StrCopy(&t, T); /* 复制T为t */
    334     Zip(S); /* 去掉S中多余的填补空余的字符 */
    335     i = (pos - 1) / CHUNKSIZE; /* 到达插入点要移动的块数 */
    336     j = (pos - 1) % CHUNKSIZE; /* 到达插入点在最后一块上要移动的字符数 */
    337     p = (*S).head;
    338     if(pos == 1) /* 插在S串前 */
    339     {
    340         t.rear->next = (*S).head;
    341         (*S).head = t.head;
    342     }
    343     else if(j == 0) /* 插在块之间 */
    344     {
    345         for(k = 1; k < i; k++)
    346             p = p->next; /* p指向插入点的左块 */
    347         q = p->next; /* q指向插入点的右块 */
    348         p->next = t.head; /* 插入t */
    349         t.rear->next = q;
    350         if(q == NULL) /* 插在S串后 */
    351             (*S).rear = t.rear; /* 改变尾指针 */
    352     }
    353     else /* 插在一块内的两个字符之间 */
    354     {
    355         for(k = 1; k <= i; k++)
    356             p = p->next; /* p指向插入点所在块 */
    357         q = (Chunk *)malloc(sizeof(Chunk)); /* 生成新块 */
    358         for(i = 0; i < j; i++)
    359             *(q->ch + i) = blank; /* 块q的前j个字符为填补空余的字符 */
    360         for(i = j; i < CHUNKSIZE; i++)
    361         {
    362             *(q->ch + i) = *(p->ch + i); /* 复制插入点后的字符到q */
    363             *(p->ch + i) = blank; /* p的该字符为填补空余的字符 */
    364         }
    365         q->next = p->next;
    366         p->next = t.head;
    367         t.rear->next = q;
    368     }
    369     (*S).curlen += t.curlen;
    370     Zip(S);
    371     return OK;
    372 }
    373 // Status StrInsert(LString *S, int pos, LString T) //插入字符串操作,有BUG
    374 // {
    375 //     //在块之间插入新的字符串,并利用zip压缩将串中多余的#去处
    376 //     int i, j, insertPos, blockPos;
    377 //     if (pos >= S->curlen) //如果pos越界,则定位在头或尾位置
    378 //         pos = S->curlen + 2;
    379 //     else if (pos <= 0)
    380 //         return ERROR;
    381 //     Chunk *h = S->head, *p, *q;
    382 //     insertPos = pos % CHUNKSIZE; //确定块中要插入的位置
    383 //     if (pos % CHUNKSIZE == 0) //如果插入的位置是块之间
    384 //     {
    385 //         blockPos = pos / CHUNKSIZE; //定位要插入块的位置
    386 //         q = S->head; //q指向头结点
    387 //         for (i = blockPos; i > 1; --i) //q指向正在被分开的块
    388 //             q = q->next;
    389 //     }
    390 //     else //如果是块中
    391 //     {
    392 //         blockPos = (pos / CHUNKSIZE) + 1; //定位块
    393 //         //将块要插入的位置前后分离
    394 //         p = (Chunk *)malloc(sizeof(Chunk)); //申请一个新的结点,将插入点后的半个结点挪过去
    395 //         q = S->head; //q指向头结点
    396 //         for (i = blockPos; i > 1; --i) //q指向正在被分开的块
    397 //             q = q->next;
    398 //         j = i = CHUNKSIZE - (pos % CHUNKSIZE); //使用i,j来存储需要挪的个数(该结点从后往前数)
    399 //         for (; i > 0; --i) //将有效字符挪到新节点中,对应原结点中的位置为#
    400 //         {
    401 //             *(p->ch + CHUNKSIZE - i) = *(q->ch + CHUNKSIZE - i);
    402 //             *(q->ch + CHUNKSIZE - i) = blank;
    403 //         }
    404 //         for (; j < CHUNKSIZE; ++j)
    405 //             *(p->ch + CHUNKSIZE - j - 1) = blank;
    406 //         p->next = q->next; //将结点重新连接起来
    407 //         q->next = p;
    408 //     }
    409 //     //此时需要在q之后插入新节点即可,插入完毕后压缩
    410 //     T.rear->next = q->next;
    411 //     q->next = T.head;
    412 //     S->curlen += T.curlen;
    413 //     Zip(S);
    414 //     return OK;
    415 // }
    416 void Zip(LString *S) //压缩串
    417 {
    418     int i, j = 0;
    419     char *q; //将字符串读入*q
    420     q = (char *)malloc(((*S).curlen + 1) * sizeof(char));
    421     Chunk *p = S->head;
    422     while(p)
    423     {
    424         for (i = 0; i < CHUNKSIZE; ++i)
    425             if (*(p->ch + i) != blank)
    426             {
    427                 *(q + j) = *(p->ch + i);
    428                 j++;
    429             }
    430         p = p->next;
    431     }
    432     *(q + j) = 0; //串结束符
    433     ClearString(S); //清空字符串S
    434     StrAssign(S, q); //将读入的字符串重新赋值给S
    435 }
    436 Status StrDelete(LString *S, int pos, int len) //删除长度为len的子串,将被删除的位置替换成为#再压缩即可
    437 {
    438     if (pos > S->curlen || pos < 1 || pos + len > S->curlen)
    439         return ERROR;
    440     Chunk *p, *q;
    441     int i, j = 0, n = 0;
    442     p = S->head;
    443     pos--;
    444     while(n < pos)
    445     {
    446         j++;
    447         if (j == CHUNKSIZE)
    448         {
    449             p = p->next;
    450             j = 0;
    451         }
    452         n++;
    453     }
    454     while(n < pos + len)
    455     {
    456         *(p->ch + j) = blank;
    457         j++;
    458         if (j == CHUNKSIZE)
    459         {
    460             p = p->next;
    461             j = 0;
    462         }
    463         n++;
    464     }
    465     Zip(S);
    466     return OK;
    467 }
    468 Status SubString(LString *Sub, LString S, int pos, int len) //返回某位置长度为len的子串
    469 {
    470     Chunk *p;
    471     char *q;
    472     if (pos > S.curlen || pos < 0 || pos + len - 1 > S.curlen)
    473         return ERROR;
    474     q = (char *)malloc((len + 1) * sizeof(char));
    475     int i = 0, j = 0, n;
    476     p = S.head;
    477     while(j < pos) //逐个位置索引到pos
    478     {
    479         if (j == pos - 1)
    480             break;
    481         ++j;
    482         ++i;
    483         if (i == CHUNKSIZE)
    484         {
    485             p = p->next;
    486             i = 0;
    487         }
    488     }
    489     j = 0;
    490     while(j < len) //逐个位置赋值
    491     {
    492         *(q + j) = *(p->ch + i);
    493         i++;
    494         if (i == CHUNKSIZE)
    495         {
    496             p = p->next;
    497             i = 0;
    498         }
    499         j++;
    500     }
    501     *(q + j) = 0;
    502     InitString(Sub); //初始化子串
    503     StrAssign(Sub, q); //将q中赋值
    504     Sub->curlen = len;
    505     return OK;
    506 }
    507 // Status SubString(LString *Sub, LString S, int pos, int len) //书上源码
    508 // {
    509 //     /* 用Sub返回串S的第pos个字符起长度为len的子串。 */
    510 //     /* 其中,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-pos+1 */
    511 //     Chunk *p, *q;
    512 //     int i, k, n, flag = 1;
    513 //     if(pos < 1 || pos > S.curlen || len < 0 || len > S.curlen - pos + 1)
    514 //         return ERROR;
    515 //     n = len / CHUNKSIZE; /* 生成空的Sub串 */
    516 //     if(len % CHUNKSIZE)
    517 //         n++; /* n为块的个数 */
    518 //     p = (Chunk *)malloc(sizeof(Chunk));
    519 //     (*Sub).head = p;
    520 //     for(i = 1; i < n; i++)
    521 //     {
    522 //         q = (Chunk *)malloc(sizeof(Chunk));
    523 //         p->next = q;
    524 //         p = q;
    525 //     }
    526 //     p->next = NULL;
    527 //     (*Sub).rear = p;
    528 //     (*Sub).curlen = len;
    529 //     for(i = len % CHUNKSIZE; i < CHUNKSIZE; i++)
    530 //         *(p->ch + i) = blank; /* 填充Sub尾部的多余空间 */
    531 //     q = (*Sub).head; /* q指向Sub串即将复制的块 */
    532 //     i = 0;  //i指示即将复制的字符在块中的位置
    533 //     p = S.head; /* p指向S串的当前块 */
    534 //     n = 0; /* n指示当前字符在串中的序号 */
    535 //     while(flag)
    536 //     {
    537 //         for(k = 0; k < CHUNKSIZE; k++) /* k指示当前字符在块中的位置 */
    538 //             if(*(p->ch + k) != blank)
    539 //             {
    540 //                 n++;
    541 //                 if(n >= pos && n <= pos + len - 1) /* 复制 */
    542 //                 {
    543 //                     if(i == CHUNKSIZE)
    544 //                     {
    545 //                         /* 到下一块 */
    546 //                         q = q->next;
    547 //                         i = 0;
    548 //                     }
    549 //                     *(q->ch + i) = *(p->ch + k);
    550 //                     i++;
    551 //                     if(n == pos + len - 1) /* 复制结束 */
    552 //                     {
    553 //                         flag = 0;
    554 //                         break;
    555 //                     }
    556 //                 }
    557 //             }
    558 //         p = p->next;
    559 //     }
    560 //     return OK;
    561 // }
    562 int Index(LString S, LString T, int pos) //在S中索引子串T
    563 {
    564     int i, j;
    565     LString sub;
    566     if (pos < 1 || pos > S.curlen - T.curlen)
    567         return ERROR;
    568     while(pos <= S.curlen - T.curlen + 1)
    569     {
    570         SubString(&sub, S, pos, T.curlen);
    571         if (StrCompare(sub, T) == 0)
    572             return pos;
    573         else
    574             ++pos;
    575     }
    576     return ERROR;
    577 }
    578 Status Replace(LString *S, LString T, LString V) //将S中的T替换成V
    579 {
    580     if (StrEmpty(T))
    581         return ERROR;
    582     int pos = 1;
    583     do
    584     {
    585         pos = Index(*S, T, pos);
    586         if (pos)
    587         {
    588             StrDelete(S, pos, T.curlen);
    589             StrInsert(S, pos, V);
    590             pos += V.curlen;
    591         }
    592     }
    593     while(pos);
    594     return OK;
    595 }

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    运行结果:

  • 相关阅读:
    【CS Round #46 (Div. 1.5) B】Letters Deque
    【CS Round #46 (Div. 1.5) A】Letters Deque
    【Codeforces Round #432 (Div. 2) A】 Arpa and a research in Mexican wave
    【Codeforces Round #432 (Div. 2) B】Arpa and an exam about geometry
    【Codeforces Round #432 (Div. 1) A】 Five Dimensional Points
    【2017 Multi-University Training Contest
    Managing remote devices
    防止表单重复提交的解决方案整理
    防止表单重复提交的解决方案整理
    日期格式化函数
  • 原文地址:https://www.cnblogs.com/hughdong/p/6910044.html
Copyright © 2011-2022 走看看