• git入门(廖雪峰老师)


     根据廖雪峰老师的git教程进行学习总结;

    1、之前上班用的都是svn进行管理,那么svn和git有什么区别呢?

      svn是集中式的版本控制系统,而git是分布式版本控制系统,那么集中式和分布式版本控制系统有什么区别呢?

      集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。(集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,宽带够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟)

      分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作得时候,就不需要联网了,因为版本库就在你自己的电脑上,既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比如说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩只需把自己的修改推送个对方,就可以互相看到对方的修改了。(和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了)

    2、在Windows上安装Git

      在Windows上使用Git,可以从Git官网直接下载:https://git-scm.com/downloads,然后按默认选项安装即可。

      安装完成后,在开始菜单里找到“Git”=》“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!

           

      安装完成后,还需要最后一步设置,在命令行输入:

    $ git config --global user.name "Your Name"
    $ git config --global user.email "email@example.com"

      注意git config命令的--global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。

    3、创建版本库

      首先,选择一个合适的地方,创建一个空目录:

    $ mkdir git
    $ cd git
    $ pwd
    /e/git

      pwd命令用于显示当前目录。我在e盘,仓库位于/e/git

      注意:windows系统中,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文

      第二步,通过git init命令把这个目录变成Git可以管理的仓库

    $ git init
    Initialized empty Git repository in E:/git/.git/

      这样Git仓库就创建好了,而且告诉你是一个空的(empty Git repository),当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。

      如果你没有看到.git目录,那是因为这个目录默认是隐藏的,用ls -ah命令就可以看见

      

    4、把文件添加到版本库

      首先,在目录下编写一个readme.txt文件,然后用命令git add告诉Git,把文件添加到仓库

    $ git add readme.txt

      然后,用命令git commit告诉Git,把文件提交到仓库:

    $ git commit -m "wrote a readme file"

      显示如下:

      

      简单解释一下git commit命令,-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便的找到改动记录。git commit命令行执行成功后会告诉你,1 file changed:一个文件被改动;2 insertions:插入了两行内容(为什么Git添加文件需要add,commit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件)

    5、查看仓库当前状态和查看修改内容

      git status命令可以让我们时刻掌握仓库当前的状态;

      如果不记得修改内容,可以使用git diff这个命令查看;

      提交修改和提交新文件一样分两步,第一步是git add;第二步是git commit -m "explain";

      

      要随时掌握工作区的状态,使用git status命令;

      如果git status告诉你有文件被修改过,用git diff可以查看修改内容

    6、版本回退

      可以使用git log命令查看历史记录,如果嫌输出的信息太多,可以加上--pretty=oneline

      

      前面一大串十六进制表示的是commit id(版本号)。

      在git中,用HEAD表示当前版本,也就是最新的提交,上一个版本就是HEAD^,上上一个版本就是HEAD^^,一次类推100个,可以简写成HEAD~100,如果只是回退到上一个版本,可以使用git reset命令:

    $ git reset --hard HEAD^

      

      查看了一下内容,果然被回退了,那么,再想来到刚刚提交最新的那个版本怎么办?

      可以找到那个版本的commit id,就可以指定回到未来的某个版本

    $ git reset --hard 39263

      版本号没必要写全,前几位就可以了,git会自动去找,

      

      又回到刚刚最新版本了。

      Git中,提供了一个命令git reflog用来记录你的每一次命令:

      

      小结:HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。

           穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本(git log --pretty=oneline)

           要重返未来,用git reflog 查看命令历史,以便确定要回到哪个版本

    7、工作区和暂存区

      Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

      

      前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:

      第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

      第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

      因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

      修改readme.txt文件,添加LICENSE文件,没有git add,使用git status查看:

      

      使用git add把两个都添加后:

      

      现在,暂存区的状态就变成这样了:

      

      git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支

      

      

    8、管理修改

      当你修改文件之后,如果git add进去了,可是没有git commit,然后又修改了这个文件,然后再git commit进去,那么其实提交的内容是第一次修改的内容,因为第二次修改没有git add进去,也就是还停留在工作区,所以你git commit之后再git status还是会提示你有一个文件没有add,可以使用git diff HEAD -- readme.txt命令查看工作区和版本库里面最新版本的区别。

      

      小结:每次修改,如果不用git add到暂存区,就不会加入到commit中。

    9、撤销修改

      当你修改了内容还没有git add的时候,你可以发现git会告诉你,git checkout -- file可以丢弃工作区的修改:

      

    $ git checkout -- readme.txt

      命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:

      一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

      一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

      总之,就是让这个文件回到最近一次git commitgit add时的状态。

        

      注意:git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout命令

      如果你进行了修改,并且git add进去了暂存区:

      

      Git同样告诉我们,用命令git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区:

      

      然后再丢弃工作区的修改:

      

      小结:

        场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file

        场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。

        场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

    10、删除文件

      一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm命令删了:

    $ rm test.txt

      这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了:

      

      现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit

      

      现在,文件就从版本库中被删除了。(提示:先手动删除文件,然后使用git rm <file>和git add <file>效果是一样的)

      另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

    $ git checkout -- test.txt

      git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

      小结:命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容

    11、远程仓库

      使用GitHub来做Git仓库托管服务。

      第一步:需要创建一个GitHub账号,由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以需要一点设置:

        ①:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsaid_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

    $ssh-keygen -t rsa -C "youremail@163.com"

      你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。

      如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsaid_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

        ②:登陆GitHub,打开“Account settings”,“SSH Keys”页面:

        然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:

        点“Add Key”,你就应该看到已经添加的Key:

      

      小结:可以把自己用到的每台电脑的key都添加到GitHub上,就可以在每台电脑上往GitHub推送了。GitHub上免费托管的Git仓库一般是任何人都可以看到的。(除非交钱,把仓库改为私有的)

    12、添加远程库

      现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。

      首先,登陆GitHub,然后,在右上角找到“new repository”按钮,创建一个新的仓库:

      在Repository name填入learngit,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库:

      目前,在GitHub上的这个learngit仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。

      现在,我们根据GitHub的提示,在本地的learngit仓库下运行命令:

    $ git remote add origina git@github.com:lsc202426/test.git
    $ git push -u origina master

      或者

    $ git remote add origin https://github.com/lsc202426/learngit.git
    $ git push -u origin master

      SSH警告:当你第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告:这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。

      Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:warning

      由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

      添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。

      看人家评论里面是这样解决的,后面再尝试一下。

      从现在起,只要本地作了提交,就可以通过命令:

    $ git push origin master

       小结:要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git;

            关联后,使用命令git push -u origin master第一次推送master分支的所有内容;

            此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改

    13、从远程库克隆

      首先,登陆GitHub,创建一个新的仓库,名字叫gitskills

      我们勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个README.md文件。创建完毕后,可以看到README.md文件:

      现在,远程库已经准备好了,下一步是用命令git clone克隆一个本地库:

      

      注意把Git库的地址换成你自己的,然后进入gitskills目录看看,已经有README.md文件了:

      

      如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https

      小结:要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。

    14、创建与合并分支

      首先,我们创建dev分支,然后切换到dev分支:

      

      git checkout命令加上-b参数表示创建并切换,相当于一下两条命令:

    $ git branch dev
    $git checkout dev
    Switched to branch 'dev'

      然后,用git branch命令查看当前分支:

      

      git branch命令会列出所有分支,当前分支前面会标一个*号。

      然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行,然后提交:

      

      现在,dev分支的工作完成,我们就可以切换回master分支:

      

      切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了,因为那个提交是在dev分支上,而master分支此刻的提交点并没有变。

      现在,我们把dev分支的工作成果合并到master分支上:

      

      git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。

      注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

      当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

      合并完成后,就可以放心地删除dev分支了:

      

      删除后,查看branch,就只剩下master分支了:

      

      因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

      小结:Git鼓励大量使用分支:

         查看分支:git branch

         创建分支:git branch <name>

         切换分支:git chcekout <name>

         创建+切换分支:git checkout -b <name>

         合并某分支到当前分支:git merge <name>

         删除分支:git branch -d <name>

    15、解决冲突

      准备新的feature1分支,继续我们的新分支开发:

      

      修改readme.txt最后一行,改为:Creating a new branch is quick AND simple.

      在featurel分支上提交:

      

      切换到master分支:

      

      Git还会自动提示我们当前master分支比远程的master分支要超前一个提交

      在master分支上把readme.txt文件的最后一行改为:Creating a new branch is quick & simple.

       

      现在,master分支和featurel分支个字都分别由新的提交变成了这样:

      

      这种情况下,Git无法执行“快速合并”,只能视图把个字的修改合并起来,但这种合并就可能会有冲突,我们试试看:

      

      果然冲突了!Git告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:

      

      我们可以直接查看readme.txt的内容:

      

      Git用<<<<<<<=======>>>>>>>标记出不同分支的内容,我们修改如下后保存:Creating a new branch is quick and simple.

      

      用带参数的git log也可以看到分支的合并情况:

      

      最后,删除feature1分支:

       

      小结:当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。

            解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。

            用git log --graph命令可以看到分支合并图。

     16、分支管理策略

      通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

      如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

      下面我们实战一下--no-ff方式的git merge

      首先,创建并切换dev分支;修改readme.txt文件并提交一个新的commit;然后切换回master;准备合并dev分支,请注意--no-ff参数,表示禁用Fast forward

    $ git merge --no-ff -m "merge with no-ff" dev
    Merge made by the 'recursive' strategy.
     readme.txt | 1 +
     1 file changed, 1 insertion(+)

      因为本次合并要创建一个新的commit,所以加上-m 参数,把commit描述写进去合并后,我们用git log看看分支历史:

    $ git log --graph --pretty=oneline --abbrev-commit
    *   e1e9c68 (HEAD -> master) merge with no-ff
    |  
    | * f52c633 (dev) add merge
    |/  
    *   cf810e4 conflict fixed
    ...

      

      在实际开发中,我们应该按照几个基本原则进行分支管理:

      首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

      那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

      你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

      所以,团队合作的分支看起来就像这样:

      小结:合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

    17、Bug分支

      软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

      当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:

    $ git status
    On branch dev
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
        new file:   hello.py
    
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
        modified:   readme.txt

      并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?

      幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

    $ git stash
    Saved working directory and index state WIP on dev: f52c633 add merge

      现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。

      首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:

    $ git checkout master
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 6 commits.
      (use "git push" to publish your local commits)
    
    $ git checkout -b issue-101
    Switched to a new branch 'issue-101'

      现在修复bug,需要把“Git is free software ...”改为“Git is a free software ...”,然后提交:

    $ git add readme.txt 
    $ git commit -m "fix bug 101"
    [issue-101 4c805e2] fix bug 101
     1 file changed, 1 insertion(+), 1 deletion(-)

      修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:

    $ git checkout master
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 6 commits.
      (use "git push" to publish your local commits)
    
    $ git merge --no-ff -m "merged bug fix 101" issue-101
    Merge made by the 'recursive' strategy.
     readme.txt | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)

      太棒了,原计划两个小时的bug修复只花了5分钟!现在,是时候接着回到dev分支干活了!

    $ git checkout dev
    Switched to branch 'dev'
    
    $ git status
    On branch dev
    nothing to commit, working tree clean

      工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看:

    $ git stash list
    stash@{0}: WIP on dev: f52c633 add merge

      工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:

      一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;

      另一种方式是用git stash pop,恢复的同时把stash内容也删了:

    $ git stash pop
    On branch dev
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
        new file:   hello.py
    
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
        modified:   readme.txt
    
    Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)

      再用git stash list查看,就看不到任何stash内容了:

    $ git stash list

      你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:

    $ git stash apply stash@{0}

      小结:修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

            当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。

     18、Feature分支

      添加一个新功能时,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。

      如果该feature分支已经add并且commit;但是还没有合并,如果想要删除:

    $ git branch -d feature-vulcan
    error: The branch 'feature-vulcan' is not fully merged.
    If you are sure you want to delete it, run 'git branch -D feature-vulcan'.

      销毁失败。Git友情提醒,feature-vulcan分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数。。

    现在我们强行删除:

    $ git branch -D feature-vulcan
    Deleted branch feature-vulcan (was 287773e).

      小结:开发一个新feature,最好新建一个分支;

            如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>强行删除。

     19、多人协作

      多人协作的工作模式通常是这样:

    1. 首先,可以试图用git push origin <branch-name>推送自己的修改;

    2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

    3. 如果合并有冲突,则解决冲突,并在本地提交;

    4. 没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!

      如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>

      这就是多人协作的工作模式。

      小结:

    • 查看远程库信息,使用git remote -v

    • 本地新建的分支如果不推送到远程,对其他人就是不可见的;

    • 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;

    • 在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;

    • 建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name

    • 从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

     20、rebase(未完待续!)

      

  • 相关阅读:
    BZOJ 2738 矩阵乘法(整体二分+二维树状数组)
    BZOJ 1430 小猴打架(prufer编码)
    BZOJ 2818 Gcd(莫比乌斯反演)
    BZOJ 4403 序列统计(Lucas)
    BZOJ 3083 遥远的国度(树链剖分+线段树)
    BZOJ 2049 [Sdoi2008]Cave 洞穴勘测(动态树)
    BZOJ 3282 Tree(动态树)
    BZOJ 3239 Discrete Logging(BSGS)
    BZOJ 2683 简单题(CDQ分治+树状数组)
    BZOJ 4327 JSOI2012 玄武密码(后缀自动机)
  • 原文地址:https://www.cnblogs.com/chao202426/p/10766016.html
Copyright © 2020-2023  润新知