精华内容
下载资源
问答
  • using namespace std; void fun1() { std::string name = "bird.png"; for (int i = 1; i < name.length(); i++ ) { if (name[i] == '.') { name = name.substr(0, i)...
    #include <iostream>
    using namespace std;
    
    void fun1()
    {
         std::string name = "bird.png";
    	for (int i = 1; i < name.length(); i++ )
    	{
    		if (name[i] == '.')
    		{
    			name = name.substr(0, i);
    			break;
    		}
    	}
    	cout<<name<<endl;
    }
    
    void fun2()
    {
         char s[50] = "";
        for(int i = 1; i <= 10; i++ )
        {
             sprintf(s, "%02d", i);
             cout << "bird" << s << ".png"<<endl;
        }
    }
    
    
    int main()
    {
       fun1();
       fun2();
       return 0;
    }
    

     

    展开全文
  • 关于后缀表达式的计算机求值以及中缀表达式转后缀表达式的分析请看我的另一篇博客——中缀表达式转前、后缀表达式及其求值后缀表达式求值#include<iostream> #include #include #include<cctype> #include using ...

    关于后缀表达式的计算机求值以及中缀表达式转后缀表达式的分析请看我的另一篇博客——中缀表达式转前、后缀表达式及其求值

    后缀表达式求值

    #include<iostream> 
    #include<iomanip>
    #include<cstdlib>
    #include<cctype> 
    #include<cmath>
    using namespace std;
    #define MaxSize 100              //堆栈元素的最大个数
    #define MaxOP 100                //运算数序列可能的最大长度
    #define Number 0                 //运算数的标识 
    #define ZERO 1.0E-10             //ZERO无限接近于0 
    typedef double ElementType;         //将堆栈元素类型具体化 
    typedef struct SNode *Stack;
    struct SNode{
        ElementType data[MaxSize];
        int top;
    };
    //建立空栈 
    Stack CreateStack()
    {
        Stack S = (Stack)malloc(sizeof(struct SNode));
        S->top = -1;
        return S;
    }
    //入栈
    void Push(Stack PtrS,ElementType X)
    {
        if(PtrS->top==MaxSize-1)
            cout<<"堆栈满"<<endl;  
        else
            PtrS->data[++(PtrS->top)]=X;
     } 
    //出栈
    #define Error -1
    ElementType Pop(Stack PtrS)
    {
        if(PtrS->top==-1){
            cout<<"空栈"<<endl;
            return Error; 
        }else{
            return (PtrS->data[(PtrS->top)--]);
        }
    }
    
    int GetOp(char s[])
    {
        /*从输入中获得下一个对象(运算符或运算数),并保存在字符数组s里*/
        /*返回值为Number或者运算符本身*/
        int i,c;
    
        /*跳过表达式前空格等字符*/
        while((s[0] = c = getchar()) == ' '||c == '\t');
    
        s[1] = '\0';
        if(!isdigit(c)&&c!='.')          //如果c是运算符 
            return c;                    //直接返回运算符 
        i = 0; 
        if(isdigit(c))                  //如果c是数字,则一直读完连续的数字并存入s 
            while(isdigit(s[++i] = c = getchar()));
        if(c == '.')                    //如果有小数点,则一直读完小数点后的连续数字 
            while(isdigit(s[++i] = getchar()));
        s[i] = '\0';                    //完成对实数字符串的存储 
    
        return Number;
     } 
    //例子 
    int main()
    {
        int Type;
        ElementType pre;
        char s[MaxOP];
        Stack Ptr = CreateStack();
    
        while((Type = GetOp(s)) != EOF){  //当未读到输入结束时 
            switch(Type){
                case Number:Push(Ptr,atof(s));  
                        break;
                case'+':Push(Ptr,Pop(Ptr)+Pop(Ptr));
                        break;
                case'*':Push(Ptr,Pop(Ptr)*Pop(Ptr));
                        break;
                case'-':pre = Pop(Ptr);
                        Push(Ptr,Pop(Ptr)-pre);
                        break;
                case'/':pre = Pop(Ptr);
                        if(fabs(pre) > ZERO)       //检查除法的分母是否为0
                            Push(Ptr,Pop(Ptr)/pre);
                        else
                            cout<<"error:zero divisor"<<endl; 
                        break;
                case'\n':cout<<setprecision(8)<<Pop(Ptr)<<endl; //小数点后最多显示8位小数(精度为8) 
                         break;
                default:cout<<"error:unknown command "<<s<<endl;
                         break;
            } 
        }
        free(Ptr);
        return 0;
    }

    中缀表达式转后缀表达式

    #include<iostream>
    #include<cstdlib>
    #include<cctype>
    using namespace std;
    #define MaxSize 100              //堆栈元素的最大个数
    #define MaxOP 100                //运算数序列可能的最大长度
    typedef char ElementType;         //将堆栈元素类型具体化
    typedef struct SNode *Stack;
    struct SNode{
        ElementType data[MaxSize];
        int top;
    };
    //建立空栈 
    Stack CreateStack()
    {
        Stack S = (Stack)malloc(sizeof(struct SNode));
        S->top = -1;
        return S;
    }
    //入栈
    void Push(Stack PtrS,ElementType X)
    {
        if(PtrS->top==MaxSize-1)
            cout<<"堆栈满"<<endl;  
        else
            PtrS->data[++(PtrS->top)]=X;
     } 
    //出栈
    #define Error -1
    ElementType Pop(Stack PtrS)
    {
        if(PtrS->top==-1){
            cout<<"空栈"<<endl;
            return Error; 
        }else{
            return (PtrS->data[(PtrS->top)--]);
        }
    }
    //优先级函数 
    int prior(char a)
    {
        if(a == '+' || a == '-')
            return 1;
        if(a == '*' || a == '/')
            return 2;
    }
    //转换函数 
    void Transfer(char a[],Stack PtrS)
    {
        int i = 0;
        while(1)
        {
            if(a[i] == '\0')  //遇到结束符
            {
                if(PtrS->top == -1)  //此时如果栈顶为空就返回
                    return;
                else
                //如果栈不为空则将栈内所有元素出栈
                {
                    while(PtrS->top != -1)
                    {
                        cout<<Pop(PtrS)<<" ";
                    }
                    return;
                }
            }
            else if(isdigit(a[i]))
            //如果是数字则直接输出
            {
                while(isdigit(a[i])){        //当a[i]是数字时,则输出连续的数字
                    cout<<a[i];
                    i++;
                }
                if(a[i] == '.'){             //如果有小数点,则一直输出完小数点后的连续数字 
                    cout<<a[i];
                    i++;
                    while(isdigit(a[i])){
                        cout<<a[i];
                        i++;
                    }
                }
                cout<<" ";
                continue;
            }
            else if(PtrS->top == -1)
            //如果不是数字,只能是运算符,判断此时堆栈有没有元素
            //如果堆栈没有元素,将运算符压入堆栈
            {
                Push(PtrS,a[i]);
                i++;
                continue;
            }
    
            else if(a[i] == '(')   //遇到左括号,直接压入堆栈
            {
                Push(PtrS,a[i]);
                i++;
                continue;
            }
            else if(a[i] == ')')   
            //遇到右括号,将堆栈中的元素弹出并输出,直到遇到左括号,最后将左括号弹出
            //左括号不输出
            {
                while(PtrS->data[PtrS->top] != '(')
                    cout<<Pop(PtrS)<<" ";
                Pop(PtrS);
                i++;
                continue;
            }
            else
            //既不是左括号,也不是右括号,堆栈也不为空
            //那么比较运算符与栈顶元素的优先级
            {
                while(1)
                {
                    if(PtrS->top == -1 || prior(a[i]) > prior(PtrS->data[PtrS->top]) || PtrS->data[PtrS->top] == '(')
                    //如果栈顶为空或者优先级大于栈顶元素或者栈顶元素是左括号
                    //那么将元素压入堆栈
                    {
                        Push(PtrS,a[i]);
                        break;
                    }
                    else
                    {
                        cout<<Pop(PtrS)<<" ";  //弹出一个元素并输出
                    }
                }
                i++;
                continue;
            }  
        }
    
        return;
    }
    
    int main()
    {
        Stack Ptr = CreateStack();
        char s[MaxOP];
        cin>>s;
        Transfer(s,Ptr);
        free(Ptr); 
        return 0;
     } 
    展开全文
  • 后缀树与后缀数组

    千次阅读 2013-10-30 21:08:36
    后缀树和后缀数组简直就是 ACM 选手必备的知识啊,我已经在两次比赛中碰到过相关的问题了。我甚至还写过一篇应用的文章,可是我真是井底之蛙啊,那时我还不知道这个叫后缀数组,还有更好的构造算法,还有很多的应用...
         后缀树后缀数组简直就是 ACM 选手必备的知识啊,我已经在两次比赛中碰到过相关的问题了。我甚至还写过 一篇应用的文章,可是我真是井底之蛙啊,那时我还不知道这个叫后缀数组,还有更好的构造算法,还有很多的应用。最近终于好好在这方面扫了个盲,在此小小地总结一下。

        假设有一个长度为 n 的字符串 T[0 ... n);S(i) 表示 T 的从下标 i 开始的后缀,即 T[i ... n)。那么 T 的 后缀数组就是把 S(i) ~ S(n - 1) 这 n 个后缀按 字典序排好序的一个数组。它对于查找 T 的子串之类的问题是很有用的。问题就在于怎样快速地把这些后缀排好序。

        最简单的方法就是把所有 S(i) 快速排序。快速排序本身的时间是 O(n log n),但是由于排序的对象是一个个字符串,所以每次比较的时间在最差情况下都会变成线性的(也就是 O(n) 的),因此总的时间在最差情况下可能会升到 O(n 2) 左右,这就很慢了。对此,我学到了三个更快的算法。

    1. Ukkonen 算法

        Ukkonen 算法先用 O(n) 的时间构造一棵 后缀树,然后再用 O(n) 的时间从后缀树得到后缀数组。在 这个网址,介绍了作者 Esko Ukkonen,并列出了他的一些论文;其中的一篇《 On-line construction of suffix-trees》是可以下载的,里面就讲解了什么是后缀树,怎样在 O(n) 的时间内构造它,以及怎样从它得到后缀数组。

        不过我一开始还没发现这篇论文,我是从 Dan Gusfield 的《Algorithms on Strings, Trees and Sequences - COMPUTER SCIENCE AND COMPUTATIONAL BIOLOGY》这本书里学到这个算法的。这本书在中国没的卖,想买的话,可以找代购网站去 Amazon 买。我是在 eMule 上搜到并下载的。这本书中的这节内容讲得还可以,虽然我觉得它示例比较少,但是花了点功夫还是看懂了。学会了之后,原作者的论文我就没有仔细看过了,所以没法评论。

       Ukkonen 算法还是比较复杂的,代码比较长;而且后缀树这个结构本身也比较费空间。总而言之,虽然该算法在理论上是最快的,后缀树也是一个很优美的结构,但是在许多实际应用中不是很实惠。

        然而,一开始我还不知道别的算法时,还是把它实现了出来( 代码 1代码 2)。(我写了两个版本,它们的不同点在于每个节点的子节点的存放方式。代码 1 是用数组,代码 2 是用链表。用数组的话,查找指定的子节点很快,只要 O(1);但是比较费空间。用链表的话,省空间,但是查找子节点比较慢,只能线性地查找,不过一般情况下问题不大。实际上,我在  PKU 3415 这道题中,用数组反而比用链表慢,可能前者分配空间所花的时间比较多吧。)

    2. DC3 算法

       我在 Google 上搜到了这篇论文,《 Linear Work Suffix Array Construction》,其中介绍了一个可以在 O(3n) 的时间内构造出后缀数组的算法,叫作 DC3 (Difference Cover mod 3) 算法。

        该算法的基本原理大致是这样的。针对所有后缀的前 3 个字符,做基数排序;对于不满 3 个字符的后缀,排序时在后面补 0(这里的 0 是结束符,在 T 中不能出现;0 的字典序最优先);排序时还要包括进从结束符(即 T[n])开始的后缀 S(n): “000”。如果所有后缀的前 3 个字符都不完全相同,那么这一次就排好了,最后去掉多余的 “000” 后缀(它一定排在第一个),就得到答案了,时间是 O(3n)。如果存在前 3 个字符相同的,则需要生成一个名次数组 R, R(i) 表示 S(i) 在排好序后位于第几名(名次从 1 开始计),接着再用上述方法递归地求 R[0 ... n] 的后缀数组,其结果和 T 的后缀数组是完全对应的,也就是说 S R(i) 排在第几位,则 S(i) 也应该排在第几位。但问题是如果这样递归层数多了,时间也就大大增加了。

        接下来,在上述算法的基础上,需要一个优化。首先,只对满足 i mod 3 = 1 或 i mod 3 = 2 的那些 S(i) 按照前 3 个字符进行基数排序;如果这其中有前 3 个字符相同的,同样也需要递归地求它们的名次数组的后缀数组。排好了 i mod 3 = 1、2 的后缀之后,就可以得到一个总的名次数组 R,其中那些 i mod 3 = 0 的后缀的名次还是未知的。接着对于所有 i mod 3 = 0 的 S(i),靠 T[i] 和 R(i + 1) 这两个关键字就可以对它们排序了。最后把排好序的 mod 3 = 1、2 和 mod 3 = 0 的后缀归并起来就是答案了。归并的时候,比较两个后缀 S(i) 和 S(j) 的方法也是看它们的前 3 个字符,如果都相同,那么比较 R(i + 1) 和 R(j + 1),若不可比(其中有一个是未知的)则再比较 R(i + 2) 和 R(j + 2)。

        有了以上的优化,即使当中出现了需要递归的情况,每次递归求解的字符串长度也只有原来的 2 / 3,那么即使递归的层数再多,总的时间之和也是会收敛的。

        以上我只是潦草地介绍一下,具体的还是自己看论文吧。论文写得还是蛮清楚的。尤其是最后有一个用 C++ 实现的代码,其中有很多细节实现地很巧妙,很值得学习。

    3. 倍增算法

        我是从 IOI 2004 国家集训队论文集中的一篇名为《 后缀数组》的文章中学到这个算法的。该文章在 Google 上 搜得到,讲得还是蛮清楚的。我在此就不多介绍了,请自己看文章。

        倍增算法最大的优点是实现简单,速度也还可以,O(n log n)。如果程序的时间要求不是很紧的话,应该作为首选的算法。 这里是我对倍增算法的实现。

    4. 多个字符串的后缀数组

        在很多问题中,都需要求多个字符串的后缀数组,也就是把多个字符串的所有后缀都放在一起排序。这个结构对于查找公共子串之类的问题是很有用的。后缀树是可以表示多个字符串的,但是 DC3 算法和倍增算法都只能求单个字符串的后缀数组。

        其实多个字符串的后缀数组可以转化成单个字符串的后缀数组。比如要求 “abc” 和 “def” 这两个字符串的后缀数组,可以转化成求 “abc1def” 的后缀数组。其中 1 是字典顺仅次于结束符 0 的字符,它也不出现在任何字符串中。这样求出来的后缀数组和 “abc” 与 “def” 的后缀数组是等价的;只是多了一个以 1 开头的后缀,但它一定排序在最前面,很容易去掉。在倍增算法中,用 0 替代 1 好像也可以;在 DC3 算法中好像不能用 0 替代 1,但是我忘记怎么重现那个错误了,所以现在也不好说。但是用 1 肯定是没错的,这样符合 “结束符在字符串中不出现” 的原则。

        这篇文章我写得比较潦草,因为我引用的几篇文章本身都写得很清楚了,我确实没有什么新发现。所以到此为止吧。


    曾经的这一天...

    我很懒的  @ 1985-08-05 15:30

        本文是 这篇文章的附件。

    /
    //Suffix Tree and Suffix Array with Ukkonen Algorithm.
    //Store child nodes in array.
    /
    #include <cstring>//strlen, memset.
    #include <list>//used by suffix tree node.
    #include <vector>//used by suffix tree.

    using namespace std;

    struct Suffix { const char* str;  int from; };

    const int ALPHABET_SIZE = 26 * 2 + 1;

    struct InNode;

    struct SfxNode //Suffix tree node
    {   const char *l, *r;//[l, r): edge label from parent to this node.
        InNode* prnt;//parent
        virtual bool isLeaf() = 0; };

    struct InNode: public SfxNode {//Internal node (non-leaf node)
        //for character X and string A, if this node's label is XA,
        //then the suffix link is the node with label A, if any.
        InNode* sfxLink;//suffix link
        SfxNode* ch[ALPHABET_SIZE];//children
        SfxNode*& child(char c)
            { if ('{post.content}' == c) { return ch[0]; }
              return c < 'a'? ch[c - 'A' + 1]: ch[c - 'a' + 27]; }
        bool isLeaf() { return false; }
    };

    struct Leaf: public SfxNode//Suffix tree leaf
    {   list<int> from;//which string does this suffix belong to.
        bool isLeaf() { return true; } };

    InNode g_internal[200000 + 100]; //Ask for memory once and allocate
    Leaf g_leaf[200000 + 100];       //them my self, to make the
    int g_inCnt = 0, g_leafCnt = 0;  //tree destruction fast.

    InNode* newInNode(const char* l = NULL, const char* r = NULL)
    {   InNode* p = &g_internal[g_inCnt++];
        p->l = l;  p->r = r;  p->sfxLink = p->prnt = NULL;
        memset( p->ch, 0, sizeof(p->ch) );
        return p; }

    Leaf* newLeaf(const char* l = NULL, const char* r = NULL)
    {   Leaf* p = &g_leaf[g_leafCnt++];
        p->l = l;  p->r = r;  p->from.clear();
        return p; }

    list<int> g_stack;//A stack for the DFS of the tree.

    class SuffixTree {
    public:
        SuffixTree(): m_root( newInNode() ), m_texts(), m_lens() {}
        ~SuffixTree() { clear(); }

        //Don't free the space of the added string
        //before the last string is added.
        void addText(const char* text) {
            m_text = m_i = text;  m_leafCnt = 0;  m_p = m_root;
            m_root->l = m_root->r = m_text;
            m_len = strlen(text);
            for (int i = 0; i <= m_len; i++) { 
                m_newIn = NULL;
                for (int j = m_leafCnt; j <= i; j++)
                    { if ( !extend(m_text + j, m_text + i) ) { break; } }
            }
            m_texts.push_back(m_text);  m_lens.push_back(m_len);
        }

        void clear() { g_inCnt = g_leafCnt = 0;  m_root = newInNode();
                       m_texts.clear();  m_lens.clear(); }

        //Write the two arrays to construct a suffix array:
        //sfx: the suffixes in lexigraphical order.
        //lcp[i]: longest common prefix of sfx[i - 1] and sfx[i].
        void toSuffixArray(Suffix* sfx, int* lcp) const {
            Node* p = m_root;
            int i = 0, depth = 0, sfxI = 0, cp = 0;
            g_stack.clear();  g_stack.push_back(0);
            while ( !g_stack.empty() ) {
                if ( p->isLeaf() ) {
                    Leaf* leaf = (Leaf*)p;
                    if (depth > 1) {
                        for (list<int>::iterator it = leaf->from.begin();
                             leaf->from.end() != it;  it++)
                        {   sfx[sfxI].from = *it;
                            sfx[sfxI].str = m_texts[*it]+m_lens[*it]-depth+1;
                            lcp[sfxI++] = cp;  cp = depth - 1; }
                    }
                    i = g_stack.back();  i++;  g_stack.pop_back();
                    depth -= p->r - p->l;  p = p->prnt;  cp = depth;
                }
                else {
                    InNode* in = (InNode*)p;
                    while ( i < ALPHABET_SIZE && !in->ch[i] ) { i++; }
                    if (ALPHABET_SIZE == i)//All children are visited.
                    {   i = g_stack.back();  i++;  g_stack.pop_back();
                        depth -= p->r - p->l;  p = p->prnt;  cp = depth; }
                    else { p = in->ch[i];  depth += p->r - p->l;
                           g_stack.push_back(i);  i = 0; }
                }
            }
        }

    private:
        typedef SfxNode Node;
        
        //Go along string m_text[l, r) starting from node p.
        void goStr(const char* l, const char* r) {
            m_i = m_p->r;
            while (l < r)
            {   m_p = ( (InNode*)m_p )->child(*l);//There must be a child.
                if (r-l <= m_p->r - m_p->l) { m_i = m_p->l + (r-l);  l=r; }
                else { m_i = m_p->r;  l += m_p->r - m_p->l; } }
        }

        //Return true if new leaf is added.
        bool extend(const char* i, const char* r) {
            if (m_i < m_p->r) {
                const char* l;
                if (*m_i == *r) {//implicit extension, no new leaf added.
                    if (*r) { m_i++;  return false; }
                    ( (Leaf*)m_p )->from.push_back( m_texts.size() );
                    l = r - (m_p->r - m_p->l - 1);
                }
                else {
                    //Insert a new internal node and add a new leaf.
                    InNode* in = newInNode(m_p->l, m_i);
                    m_p->prnt->child(*m_p->l) = in;  in->prnt = m_p->prnt;
                    in->child(*m_i) = m_p;  m_p->prnt = in;  m_p->l = m_i;
                    Leaf* leaf = newLeaf(r, m_text + m_len + 1);
                    in->child(*r) = leaf;  leaf->prnt = in;
                    leaf->from.push_back( m_texts.size() );  m_leafCnt++;
                    //This new internal node may be suffix link of others.
                    if (m_newIn) { m_newIn->sfxLink = in; }
                    m_p = m_newIn = in;
                    l = r - (m_p->r - m_p->l);
                }
                //Find the position of next extension.
                InNode* p = m_p->prnt;  m_p = p;
                if (p->sfxLink) { m_p = p->sfxLink; } else { l++; }
                goStr(l, r);
            }
            else {//in condition that m_i == m_p->r
                InNode* p = (InNode*)m_p;//now m_p must be internal.
                if (m_newIn) { m_newIn->sfxLink = p;  m_newIn = NULL; }
                Node* ch = p->child(*r);
                if (ch)
                {   if (*r) { m_p = ch;  m_i = m_p->l + 1;  return false; }
                    ( (Leaf*)ch )->from.push_back( m_texts.size() ); }
                else { Leaf* leaf = newLeaf(r, m_text + m_len + 1);
                       p->child(*r) = leaf;  leaf->prnt = p;  m_leafCnt++;
                       leaf->from.push_back( m_texts.size() ); }
                if (i < r) { m_p = p->sfxLink;  goStr(NULL, NULL); }
            }
            return true;
        }

        InNode* m_root;
        vector<const char*> m_texts;  vector<int> m_lens;
        //the following members are to help the extensions.
        Node* m_p;  InNode* m_newIn;
        const char *m_i, *m_text;
        int m_leafCnt, m_len;
    };

    //Test suite and usage example
    #include <iostream>
    int main() {
        Suffix sa[100];  int lcp[100];
        char a[] = "xabxa", b[] = "babxba";
        SuffixTree t;  t.addText(a);  t.addText(b);
        t.toSuffixArray(sa, lcp);
        int cnt = strlen(a) + strlen(b);
        for (int i = 0; i < cnt; i++)
        {   cout << sa[i].from <<" "<< sa[i].str <<" "<<lcp[i] << endl; }
        return 0; //output: 0 a 0
                  //        1 a 1
                  //        0 abxa 1
                  //        1 abxba 3
                  //        1 ba 0
                  //        1 babxba 2
                  //        0 bxa 1
                  //        1 bxba 2
                  //        0 xa 0
                  //        0 xabxa 2
                  //        1 xba 1
    }



    我很懒的  @ 1985-08-05 17:04

        本文是 这篇文章的附件。

    /
    //Constructing Suffix Array with Doubling Algorithm, O(n log n).
    /
    #include <algorithm>//sort
    #include <cstring>//memset

    using namespace std;

    const int MAX_SFX = 210000;

    struct Sfx {
        int i;  int key[2];
        bool operator < (const Sfx& s) const
        {   return key[0] < s.key[0]
                   || key[0] == s.key[0] && key[1] < s.key[1]; }
    };

    int g_buf[MAX_SFX + 1];
    Sfx g_tempSfx[2][MAX_SFX], *g_sa = g_tempSfx[0];

    void cSort(Sfx* in, int n, int key, Sfx* out) {
        int* cnt = g_buf;  memset( cnt, 0, sizeof(int) * (n + 1) );
        for (int i = 0; i < n; i++) { cnt[ in[i].key[key] ]++; }
        for (int i = 1; i <= n; i++) { cnt[i] += cnt[i - 1]; }
        for (int i = n - 1; i >= 0; i--)
            { out[ --cnt[ in[i].key[key] ] ] = in[i]; }
    }

    //Build a suffix array from string 'text' whose length is 'len'.
    //write the result into global array 'g_sa'.
    void buildSA(char* text, int len) {
        Sfx *temp = g_tempSfx[1];
        int* rank = g_buf;
        for (int i = 0; i < len; i++)
            { g_sa[i].i = g_sa[i].key[1] = i;  g_sa[i].key[0] = text[i]; }
        sort(g_sa, g_sa + len);
        for (int i = 0; i < len; i++) { g_sa[i].key[1] = 0; }
        int wid = 1;
        while (wid < len) {
            rank[ g_sa[0].i ] = 1;
            for (int i = 1; i < len; i++)
            {   rank[ g_sa[i].i ] = rank[ g_sa[i - 1].i ];
                if ( g_sa[i-1] < g_sa[i] ) { rank[ g_sa[i].i ]++; } }
            for (int i = 0; i < len; i++)
                { g_sa[i].i = i;  g_sa[i].key[0] = rank[i];
                  g_sa[i].key[1] = i + wid < len? rank[i + wid]: 0; }
            cSort(g_sa, len, 1, temp);  cSort(temp, len, 0, g_sa);
            wid *= 2;
        }
    }

    int getLCP(char* a, char* b)
    { int l=0;  while(*a && *b && *a==*b) { l++; a++; b++; }  return l; }

    void getLCP(char* text, Sfx* sfx, int len, int* lcp) {
        int* rank = g_buf;
        for (int i=0, r=0; i < len; i++, r++) { rank[ sfx[i].i ] = r; }
        lcp[0] = 0;
        if (rank[0])
            { lcp[ rank[0] ] = getLCP( text, text + sfx[ rank[0]-1 ].i ); }
        for (int i = 1; i < len; i++) {
            if ( !rank[i] ) { continue; }
            if (lcp[ rank[i - 1] ] <= 1)
            { lcp[ rank[i] ] = getLCP( text+i, text+sfx[ rank[i]-1 ].i ); }
            else
            { int L = lcp[ rank[i - 1] ] - 1;
              lcp[rank[i]] = L+getLCP(text+i+L, text+sfx[rank[i]-1].i+L); }
        }
    }

    //Test suite and usage example
    #include <iostream>
    using namespace std;
    int main() {
        char str[] = "aabbaa{post.content}ababab";
        int from[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1};
        int lcp[13];
        buildSA(str, 13);  getLCP(str, g_sa, 13, lcp);
        for (int i=1; i<13; i++)//The first suffix is useless (empty).
        { cout<<from[g_sa[i].i]<<' '<<str+g_sa[i].i<<' '<<lcp[i]<<endl; }
        return 0;//output: 0 a 0
                 //        0 aa 1
                 //        0 aabbaa 2
                 //        1 ab 1
                 //        1 abab 2
                 //        1 ababab 4
                 //        0 abbaa 2
                 //        1 b 0
                 //        0 baa 1
                 //        1 bab 2
                 //        1 babab 3
                 //        0 bbaa 1
    }


    展开全文
  • 算法学习:后缀自动机转后缀树转后缀数组 引入 其实这是一篇水文 想要学后缀自动机的话去查2012年noi冬令营陈立杰讲稿 顺便说一句,讲稿上有一些错误,多翻几篇博客加深理解。 今天这里主要要讲的是后缀...

    算法学习:后缀自动机转后缀树转后缀数组

    引入

    其实这是一篇水文
    想要学后缀自动机的话去查2012年noi冬令营陈立杰讲稿
    顺便说一句,讲稿上有一些错误,多翻几篇博客加深理解。
    今天这里主要要讲的是后缀自动机如何转后缀树和后缀数组

    后缀树

    这里首先需要提一下后缀树的概念
    后缀树其实相当于把所有的后缀插入一颗Trie树
    然而时空复杂度都是O(N^2)的
    所以我们可以把这可Trie树上的某些边压缩一下
    比如这样

    这是一颗字符串banans的所有后缀组成的Trie树

    这是一颗压缩后的Trie树,也就是后缀树
    (图片来源于网络)

    从后缀自动机到后缀树

    其实操作非常简单,就是把字符串倒序插入后缀自动机,形成的parent树就是后缀树
    证明的话,考虑一对parent树上的父子关系。
    AAAAxAAAAxBAAx
    考虑这样一个字符串
    考虑串AAAAx和AAx
    显然AAx是AAAAx parent树上的父亲
    我们发现,AAx是AAAAx的后缀
    我们考虑parent树上从叶子节点到根的路径。
    每个节点表示的字符串长度集合[Min(s),Max(s)]越来越小,而且我们还会发现,父亲一定是儿子的后缀。并且儿子的Right集合是父亲的真子集。也就是父亲可能作为别的儿子的后缀
    这个时候我们对比后缀树。
    仍然是考虑一条从叶子节点到根的路径,我们会发现父亲一定是儿子的前缀。并且一个父亲可能作为很多个儿子的前缀,比如上面例子中的AAx可以作为AAAx,AAAAx,BAAx的后缀。
    你瞧,这不是和后缀自动机反着来的么!
    那么我们把字符串逆序,是不是意味着所有的前缀变后缀啦?
    那么插入后缀自动机中,形成的Parent树不就是一个逆序前的后缀树吗?
    这个时候,我们考虑对应关系。
    还是原来AAx和AAAAx的例子
    我们发现,AAx和AAAAx差了一个“AA”,BAAx和AAx差了一个“B”
    这个时候“AA”不会再分叉出去接别的后缀,说明,这条边是可以压缩的。
    那么如果我们的字符串已经逆序,AA再原串上就不会再去接别的前缀。
    那么AA就可以压缩。
    也就是后缀树上的压缩边啦。
    那么AA是什么?
    我们显然可以在插入后缀自动机的时候记录AAx的位置pos,假设AAx表示的节点为u,AAAAx表示的节点为v,那么AA对应在原串中就是pos+(Max(v)-Max(u))~pos+Max(v)
    在后缀树上这就是一条边的标记。因为这条边是可压缩的,所以只要记录这条边的首字符pos+(Max(v)-Max(u))即可。

    后缀树转后缀数组

    你一定好奇我为什么没有先贴代码。
    先不着急,代码可以一起贴。
    请你先自己yy一下,如果给你可以后缀树,怎么求后缀数组。
    后缀树是一颗Trie树。
    是一颗Trie树
    Trie树!!!
    那不就从根节点开始,按字母字典序dfs搜一下后缀树就可以了吗?
    你可能会想:哦,原来这是后缀数组的另外一种求法。不过和原来的算法没差。
    真的没差?
    原来的方法是倍增法,复杂度O(nlogn)
    可是后缀自动机的复杂度是线性的,线性的,线性的!!!
    (好吧我得承认它常数跑得太满了,再数据量不大的情况下还是更慢的,比较鸡肋)
    现在真的可以贴代码了

    代码

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<cmath>
    using namespace std;
    const int N = 1e6 + 10; 
    int sa[N], rank[N], s[N], n;
    void readchar() {
        char ch = getchar(); int x = 0;
        while(ch < 'a' || ch > 'z') ch = getchar();
        while(ch >= 'a' && ch <= 'z') {s[++x] = ch - 'a'; ch = getchar();}
        n = x;
    } 
    struct SAM {
        int ch[N][26], pos[N], len[N], fa[N], last, sz;
        bool val[N];
        SAM() {last = ++sz;}
        void Extend(int c, int po) {
            int p = last, np = last = ++sz;
            pos[np] = po; val[np] = true; len[np] = len[p] + 1;
            for(;p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
            if(!p) fa[np] = 1;
            else {
                int q = ch[p][c];
                if(len[q] == len[p] + 1) fa[np] = q;
                else {
                    int nq = ++sz; len[nq] = len[p] + 1;
                    memcpy(ch[nq], ch[q], sizeof(ch[q]));
                    fa[nq] = fa[q]; pos[nq] = pos[q];
                    fa[q] = fa[np] = nq;
                    for(;ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
                }
            } 
        }
    }sam;
    
    struct Suffix_Tree {
        int ch[N][26], pos[N], tp;
        bool val[N];
        void Add(int u, int v, int c) {ch[u][c] = v;}
        void Build() {
            for(int i = 1;i <= sam.sz; ++i) val[i] = sam.val[i], pos[i] = sam.pos[i];
            for(int i = 2;i <= sam.sz; ++i) 
                Add(sam.fa[i], i, s[pos[i] + sam.len[sam.fa[i]]]);
        }
        void Dfs(int u) {
            if(val[u]) sa[rank[pos[u]] = ++tp] = pos[u];
            for(int i = 0 ;i < 26; ++i) 
            if(ch[u][i]) Dfs(ch[u][i]);
        }
    }suftree;
    
    int main() {
        readchar();
        for(int i = n; i >= 1; --i) sam.Extend(s[i], i);
        suftree.Build();
        suftree.Dfs(1);
        for(int i = 1;i <= n; ++i) printf("%d ", sa[i]);
        putchar('\n');
        return 0;
    }

    是的,你没有看错,就算是以博主(我我我!)这样很常(la)数(ji)的代码也只要60行
    舒服!

    展开全文
  • 后缀数组

    2019-05-21 16:59:20
    后缀数组 Suffix Array 将字符串S的所有后缀按字典序排序后得到的数组#include #include #include #include #include using namespace std; const int MAXN = 100; int n, k; int ranks
  • 后缀表达式

    千次阅读 2017-12-16 19:38:40
    所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。 如:3*(5–2)+7对应的后缀表达式为:...
  • C++中的using namespace std的作用

    万次阅读 多人点赞 2018-09-09 20:19:54
    C++中的using namespace std的作用  所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。  iostream和iostream.h的区别:  后缀为.h的头文件C++标注...
  • 字符串-后缀树和后缀数组详解

    千次阅读 2021-05-15 16:17:16
    后缀树和后缀数组可以解决大部分字符串问题。光速从入门到放弃,看这一篇就够了。 例题: HDU-1403最长公共子串 洛谷P2408 不同子串个数 HDU-5769Substring
  • std命名空间

    千次阅读 2019-05-15 19:05:00
    C++ std命名空间 1. 什么是命名空间 在编程语言中,命名空间是一种特殊的作用域,它包含了处于该作用域中的所有标示符,而且其本身也是由标示符表示的。命名空间的使用目的是为了将逻辑相关的标示符限定在一起,...
  • 分组height?半个月没动后缀数组一堆名词都忘了233#include #include #include #define rep(i,j,k) for(i=j;i;i++) #define FOR(i,j,k) for(i=j;i;i++) using std::min; using std::max; #define N
  • 后缀

    2013-09-22 12:50:43
    这道题是codechef上九月月赛的一道压轴题目,题意很简单,就是时时刻刻求一个字符串的所有不同字串个数,支持动态末尾添加... 无论是后缀数组还是后缀树或者是后缀自动机,他们可以说都是处理字符串题目的一个非常强
  • 什么是后缀自动机 温馨提醒:以下概念比较晕人,请保持耐心. 后缀自动机是一个有向无环图,节点为状态,有向边为状态转移。其中有一个初始状态可以到达所有状态,若干个结束状态,从初始状态走到一个结束状态,...
  • 后缀自动机 转变为 后缀数组

    千次阅读 2016-10-05 10:19:42
    像我这只会写后缀自动机的选手遇到LCP相关的问题就一脸无奈,而用后缀数组可以解决这样的问题。而且,不管怎么讲,还是后缀自动机时间复杂度低啊!要是能用后缀自动机弄出来后缀数组那就非常妙了(好,常数这个东西...
  • c++学习笔记 std的作用

    2019-06-03 00:03:18
    C++学习笔记 std的作用1.直接指定标识符2.使用using关键字3.使用using namespace std C++编程时几乎每次都敲...早些的编码将标准库功能定义在全局空间里(所以并不需要注明使用什么命名空间),声明在带.h后缀的头文...
  • using namespace std

    2014-05-16 07:06:14
    using namespace std 编辑 所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。 目录 1区别介绍 ...前者没有后缀
  • 后缀数组详解

    2018-08-18 11:39:38
    后缀数组详解 什么是后缀数组 后缀数组是处理字符串的有力工具 —罗穗骞 个人理解:后缀数组是让人蒙逼的有力工具! 就像上面那位大神所说的,后缀数组可以解决很多关于字符串的问题, 譬如P3809 【模板】...
  • std::string

    2019-01-08 15:37:33
    C++/C++11中std::string是个模板类,它是一个标准库。使用string类型必须首先包含头文件。作为标准库的一部分,string定义在命名空间std中。 构造函数初始化 #include &amp;lt;iostream&amp;gt; #include...
  • C++ 命名空间std

    2015-03-08 21:38:45
    1、命名空间std  C++标准中引入命名空间的概念,是为了解决不同模块或者函数库中相同标识符冲突的问题。有了命名空间的概念,标识符就被限制在特定的范围(函数)内,不会引起命名冲突。最典型的例子就是std命名空间...
  • using namespace std 说明

    千次阅读 2016-06-05 23:29:18
    using namespace std 此句含义是引用std命名空间,因为此空间包含大量的命名,例如cin 、cout;但是我们要避免使用这种引用,因为他把所有的命名都引用过来,容易和自己起的名字重复,为了避免产生这种错误,用到...
  • OSG C++ 获取文件后缀 OSG修改文件后缀名: 在使用OSG开发时,可以使用OSG提供的接口快速实现文件后缀名的修改,而不用麻烦地自己去写C++代码。 在osg的osgDB/FileNameUtils库中,提供了获取和修改文件后缀的函数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 64,705
精华内容 25,882
关键字:

后缀std