Maze

今天看到这道搜索题,发现很久没写bfs都不熟练了。。。

由于要求字母不能重复用,因此还是和普通BFS DFS一样,visit数组来判断是否访问,只在匹配到了字符串才置对应位为1,

BFS里面由于增加了匹配,因此如果未匹配应该是取下一个queue的内容,continue到下一轮循环,之前把pop写到后面去了,导致死循环,front() 和pop()以后要紧跟,因为后面可能加些访问
之类的东西,例如这里加了一个如果当前不match就return,导致只peek却没有pop。

dfs也是一样,忘记了不match直接return,以为不加也是一样,其实如果不加的话,可能当前不匹配但是后面扩展又匹配导致出错甚至死循环,由于dfs是递归算法,因此只能设置标志位
来判断是否match了,因此效率应该是不如bfs的。

#include <cstring>
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
int n,m;
char str[100+1];
char mat[22][22];
bool visit[22][22];
int dfs_stri;
bool dfs_found;
bool search_bfs()
{
    int stri;
    pair<int, int> p;
    for(int i=0;i<=n-1;i++)
    {
        for(int j=0;j<=m-1;j++)
        {
            for(int i=0;i<=n-1;i++)
                memset(visit[i],0,sizeof(bool)*m);
            stri=0;
            if(mat[i][j]!=str[stri])
                continue;
            queue<pair<int, int> > q;
            q.push({i,j});
            while(!q.empty())
            {
                p=q.front();
                q.pop();
                if(mat[p.first][p.second]==str[stri])
                    stri++,visit[p.first][p.second]=1;
                else
                    continue;
                if(!str[stri])
                {
                    //cout<<i<<" "<<j<<" "<<p.first<<" "<<p.second<<endl;
                    return true;
                }

                if(p.first-1>=0 && visit[p.first-1][p.second]==false && mat[p.first-1][p.second]==str[stri])
                    q.push({p.first-1,p.second});

                if(p.first+1<=n-1 && visit[p.first+1][p.second]==false && mat[p.first+1][p.second]==str[stri])
                    q.push({p.first+1,p.second});
                if(p.second-1>=0 && visit[p.first][p.second-1]==false && mat[p.first][p.second-1]==str[stri])
                    q.push({p.first,p.second-1});
                if(p.second+1<=m-1 && visit[p.first][p.second+1]==false && mat[p.first][p.second+1]==str[stri])
                    q.push({p.first,p.second+1});   

            }
        }
    }
    return false;
}
void dfs(pair<int, int> p)
{
    if(mat[p.first][p.second]==str[dfs_stri])
        dfs_stri++,visit[p.first][p.second]=1;
    else
        return ;
    if(!str[dfs_stri])
    {
        dfs_found=true;
        return ;
    }
    if(p.first-1>=0 && !visit[p.first-1][p.second] && mat[p.first-1][p.second]==str[dfs_stri])
        dfs({p.first-1,p.second});
    if(p.first+1<= n-1 && !visit[p.first+1][p.second] && mat[p.first+1][p.second]==str[dfs_stri])
        dfs({p.first+1,p.second});
    if(p.second-1>=0 && !visit[p.first][p.second-1] && mat[p.first][p.second-1]==str[dfs_stri])
        dfs({p.first,p.second-1});
    if(p.second+1<= m-1 && !visit[p.first][p.second+1] && mat[p.first][p.second+1]==str[dfs_stri])
        dfs({p.first,p.second+1});
}
bool search_dfs()
{
    for(int i=0;i<=m-1;i++)
    {
        for(int j=0;j<=n-1;j++)
        {
            for(int k=0;k<=n-1;k++)
                memset(visit[k],0,sizeof(visit[k]));
            dfs_stri=0;
            dfs_found=false;
            dfs(make_pair(i,j));
            if(dfs_found)
            {
                cout<<i<<" "<<j<<endl;
                return true;
            }
        }
    }
    return false;
}

/*
bool search()
{
    int si, sj,stri;
    for(int i=0;i<=n-1;i++)
    {
        for(int j=0;j<=m-1;j++)
        {
            si=i,sj=j;
            stri=0;
            while(str[stri])
            {
                if(si-1>=0 && mat[si-1][sj]==str[stri])//up
                    stri++,si--;
                else if(si+1<=n-1 && mat[si+1][sj]==str[stri])//down
                    stri++, si++;   
                else if(sj-1>=0 && mat[si][sj-1]==str[stri])//left
                    stri++,sj--;
                else if(sj+1<=m-1 && mat[si][sj+1]==str[stri])//right
                    stri++,sj++;
                else
                    break;
            }
            //if(str[stri])
            //    return 0;
            if(!str[stri])
            {
                cout<<i<<" "<<j<<endl<<si<<" "<<sj<<endl;
                return true;
            }
        }
    }
    return false;
}*/

int main()
{
    freopen("maze.in","r",stdin);
    scanf("%d%d ",&n,&m);
    //gets(str);
    //cout<<str<<endl;
    for(int i=0;i<n;i++)
        scanf("%s ",mat[i]),cout<<mat[i]<<endl;

    /*
    if(search_bfs())
        puts("YES");
    else
        puts("NO");
    */
    while(gets(str))
    {
        cout<<str<<endl;
        if(search_dfs())
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}

今天在外面看到vogue这个词,查了知道是时尚,时髦的意思,在自行车上很多

Posted by richard爱闹 - 9月 6 2014
如需转载,请注明: 本文来自 Richard

LPS

今天是Admis算法讨论班第一次课,回顾了一下最长回文子串(LPS)问题以及KMP,LPS的马拉车(瞿神起的中文名)算法 是比较难的算法。

之前看了邹博PPT写了一次,一次AC,但是第一次写是比较卡的。

后来今天写发现很不熟练,而且debug了半天,然后发现里面if漏掉了判断i在mx左右就去判断mx-i和P[j]的关系。代码如下:

void LPS()//2n+1
{
    P[0]=1;//len at least 1
    id=0, mx=id+P[id], maxp=0,maxi=0;
    int len=newstr.size();
    for(int i=1;i<=len-1;i++)
    {
        j=2*id-i;
        if(mx>i)
        {
            if(mx-i>=P[j])
                P[i]=P[j];
            else
            {
                P[i]=mx-i;
            }
        }
        else
            P[i]=1;
        while(i+P[i]<=len-1 && i+P[i]>=0 && i-P[i]<=len-1 && i-P[i]>=0 && newstr[i+P[i]]==newstr[i-P[i]])
            P[i]++;

        if(mx<i+P[i])
        {
            id=i;
            mx=id+P[id];
        } 
        if(maxp<P[i]-1)
            maxp=P[i]-1,maxi=i;
    }
    //for(int i=0;i<=len-1;i++)
    //    cout<<P[i]<<" ";
}

void Extend()
{
    string jing="#";
    newstr="#"; 
    for(int i=0;i<str.size();i++)
    {
        newstr+=str[i];
        newstr+=jing;
        //newstr+=str[i];
        //newstr+="#";
    }
    //cout<<newstr<<endl;
}

后来写了一个朴素的枚举中心位置,如果每次等到确定了位置再去判断回文串就是O(n^3), 其实可以每一次extend一个字符都判断是否回文了,这样还是O(n^2),代码如下

string longestPalindrome_n2_enum(string s)
    {
        str=s;
        Extend();
        int maxextlen=0, centeri=0,extlen;
        int len=newstr.size();
        for(int i=0;i<len;i++)
        {
            extlen=0;
            while(i-extlen>=0 && i+extlen<=len-1 && newstr[i-extlen]==newstr[i+extlen])
                extlen++;
            extlen--;
            if(maxextlen<extlen)
                centeri=i,maxextlen=extlen;
        }
        string withjing=newstr.substr(centeri-maxextlen, 2*maxextlen+1);
        string result;
        for(int i=0;i<withjing.size();i++)
        {
            if(withjing[i]!='#')
                result+=withjing[i];
        }
        return result;
    }

今天肥站提出需要修改一下最后输出结果的处理,直接定位原串位置,

似乎奇偶串要分开处理,于是以下代码
if(newstr[maxi]==’#’)
return str.substr((maxi-1)/2-(P[maxi]-1)/2+1,P[maxi]-1);
else
return str.substr(maxi/2-(P[maxi]-1)/2,P[maxi]-1);

后来站站说只有一个语句,似乎是的。
return str.substr(maxi/2-(P[maxi]-1)/2,P[maxi]-1);

今天豆豆提到了DP算法,我之前看到过,但是一位是上面的enum算法,后来看了发现不是的,DP(i,j)=DP(i+1,j-1) if(s[i]=s[j])
于是打算写一下。虽然LeetCode一次AC,因为时间卡的不严,像hihocoder第一题必须是最优的马拉车算法,记得微博上cici埕好像也是直接一A,只是一开始编译器选错了。

里面需要注意的是base和递推关系,这是DP的关键。这里递推的长度是间隔2的,所以base必须是长为1和2的状态都要算出来,然后奇偶交替递推。
然后就是递推状态之间是长度小的决定长度大的,因此外层for一定要长度从小到大过去,和矩阵链一样。第一次run又挂了, 又是二维数组开打了,每次都被这个吓得代码没自信了。

string longestPalindrome_n2_dp(string s)
    {
        int maxi=0, maxj=0,n=s.size();
        for(int i=0;i<=n-1;i++)
            memset(dp[i],0,sizeof(dp[i]));
        for(int i=0;i<=n-1;i++)
            dp[i][i]=1;//len=1
        for(int i=0;i<=n-2;i++)
        {
            if(s[i]==s[i+1])//len=2
                dp[i][i+1]=1,maxi=i,maxj=i+1;
        }
        for(int len=3;len<=n;len++)
        {
            for(int i=0;i<=n-1;i++)
            {
                int j=i+len-1;
                if(j>=n)
                    break;
                if(s[i]==s[j])
                    dp[i][j]=dp[i+1][j-1];
                if(dp[i][j])
                    maxi=i,maxj=j;
            }
        }
        return s.substr(maxi, maxj-maxi+1);
    }

上面的代码都在LeetcodeAC了,再加上暴力的枚举位置,外加结束再判断回文串的O(n^3), 一共是豆豆所说的4种算法。提倡一题多解,训练代码能力。

Posted by richard爱闹 - 9月 4 2014
如需转载,请注明: 本文来自 Richard

Catalan number

当年组合数学里面的计数章节,生成函数。包括Knuth的具体数学里都是经典章节。

1/(n+1) X C(2n,n)

经典例子是n个结点的二叉树个数,很容易写出递推方程来。

f(n)=sum(f(k)*f(n-1-k)), 0<=k<=n-1

其中的1是root,递推出口或者base是f(0)=1, 也即空树只有一个,然后就可以一路递推过去了。

但是括号匹配,后者收BOP上的买票找零问题,似乎比这个的递推方程要复杂一些。

n对括号,顺序不知,揉在一起,问有多少种合法序列。咋一样括号,第一反应都是栈,但这题不是。

我按照BOP上的思路复述一遍。

0,1,2,…..2n-1

第一个一定是( 不然出错,然后后面找到一个与他匹配的), 这个)的index一定是奇数。因为如果是偶数,那么中间夹杂了奇数个括号对,
不能成为合法的括号序列。因此将问题划分为设为index为k,那么中间1…k-1, 后面k+1…2n-1 都是合法序列对,划分为两个子问题。由于括号对是
和其实位置无关的,因此用长度一维就可以表示了。

f(2n)=sum(f(k-1)f(2n-1-(k+1)+1))=sum(f(k-1)f(2n-k-1)) 由于k是奇数,不利于k的枚举,因此设k=2*i+1, i=0,…n-1

f(2n)=sum(f(2i)*f(2n-2i-2)) i=0….n-1

上面式子用下变量替换就是前面二叉树计数的例子了,具体求解在严奶奶的DS书上

string a, b;
a+=(b[i]+”#”)是不可以的,因为后者是char+ char* , string没有重载这个参数列表的+运算,string jin=”#”, 这样是可以的,+= + 的左边都最好是string类型。
http://www.cplusplus.com/reference/string/string/operator+/

开的数组大小是10W的数量级大小,百万级程序会挂掉,如果在class里面

Manacher算法的时间复杂度分析见这里 http://www.douban.com/note/321468038/

http://hihocoder.com/contest/hiho1/problem/1 hihocoer题目,还有leetcode也有,不过leetcode需要返回串,需要维护maxi

http://www.cnblogs.com/BigBallon/p/3816890.html?ADUIN=837626197&ADSESSION=1409747372&ADTAG=CLIENT.QQ.5329_.0&ADPUBNO=26349 一个HUST学弟的博客,他的代码有点区别,
前面加了一个$, 感觉使得复杂了,我倾向于邹波的版本

每次弄路由器都会出现各种问题,现在应该总结下各种问题,科研人员有点渣啊。。。。
复旦宿舍上网(Internet)几种方式

  1. 绑定静态IP,宿舍楼下,然后设置代理(有些实验室有免费代理,一般需要将宿舍IP加到代理里面才可以用,另一种是每个人都有的复旦图书馆代理,proxy.fudan.edu.cn)或者使用VPN(可能
    只有少数同学认识计算机系或者信息办的同学才有的)

  2. 购买宽带上网,北区非洲街,花钱,然后新建宽带连接,输入账号密码,属于铁通网络,是外网,因此访问内网的时候需要断宽带,比较麻烦,不如用图书馆代理方便

3.使用路由器。单端口路由器(连接宿舍端口),连接路由Wifi,然后设置无线的IP和子网掩码(总是忘记,一般是192.168.1.1-255随便选一个非路由IP的,只要不和路由IP冲突就可以了),不然和路由器不是一个子网(192.168.*)的,无法连上路由设置。双端口可以无线,也可以有线连接

  1. 无线路由器,双端口可以无线和有线连接路由器,然后设置里面选择PPPoE方式,然后输入账号密码,就可以连接了

Posted by richard爱闹 - 9月 3 2014
如需转载,请注明: 本文来自 Richard

Trie based string match

最近经常遇到WinEdt dvi->pdf文件过程出现 error unable to open “*.pdf” 的错误,然后我一般都是打开资源管理器,然后找到这个文件对应的句柄,然后把对应的process kill掉,
不知道大家有没有更好的解决方案。

之前这道题目一直掐在这里,当时是偶然看到vjudge这个融合几个OJ的一个神奇OJ,然后一个HUST的学弟给我发的链接。

但是第一道题目发现比Trie的裸题稍微加了点东西,之前有一个大致的朴素算法也是每个后缀去match这个字典树,但是当时好像是发现这个做法对于是否能够处理keyword之间有前缀关系
产生了怀疑还是什么,然后就停在那里,没有继续做了。

后来发给瞿神,瞿神也是这个思路,但是没A。

最后还是在光环无限的fawkes大神的指导下,A了这道题目。首先答案是0~N的范围,N是keyword数这点和题意是契合的,但是fawes大神考虑到keyword是否可能出现重复,于是就要稍微改改代码了。果然在大神指导下A了这道题目。

附上代码:里面有几个地方的改进是在大神指导下完成的。

  1. 首先传后缀只要传入一个指针,这样的话就需要转成char 传进去,但是c_str()是const char 要注意。

  2. 然后之前有个bug就是遍历trie的指针,和query 字符串的遍历都要考虑是否越界,我当时以为肯定trie先遍历完,于是query不会越界,但是后来发现其实字符串如果trie里有一个keyword是query的前缀,
    那么就会出现字符串先越界,所以要特别当心,这个还是代码经验不够丰富导致的,我一眼还看不出bug,超珲立马就看出来了。

  3. 然后就是判断字符串是否越界,用了strlen,但是大神强调这是O(n),用判断是否\0处理,同时当时写的快了,漏了一个bool 非来判断字符串遍历结束。

  4. 如果是处理keyword重复的话,就是每个count含义和之前自己定义的不同了,之前是为了计算匹配了前缀的keyword数方便,在构建trie的时候,就每次往下一个keyword当前count就
    ++,计算的是子树里keyword的个数。现在是当前keyword在字典里有几个。于是再每次遍历完一个keyword的时候,就将其count++就可以了,两个for循环只要第一个for结束之后count++就可以了,表示最后一个节点的count++,正好对应了keyword的count。

5.还有一个细节也是代码不熟练,我一直纠结如何避免重复的匹配,以为题目keyword只有是否出现的概念,多个keyword也是如此,超珲大神用isword=false来加锁,这样第一次count累加了之后之后再match就不会多累加了。然后ans全局变量作为累加值,其实主要是这个加锁的思想没有立马反应过来。

按照瞿神的说法,这道题目的数据不够强,如果强的话,需要AC自动机,虽然我还不会。。。

http://vjudge.net/contest/view.action?cid=50652#problem/F

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
struct node
{
    bool isword;
    node* child[26];
    int count;
    node()
    {
        for(int i=0;i<26;i++)
            child[i]=NULL;
        isword=false;
        count=0;
    }
};

node root;
string dict[10000];
int n,m;
string query;

void CreateTrie()
{
    root.isword=false;
    for(int i=0;i<26;i++)
        root.child[i]=NULL;
    for(int i=0;i<n;i++)
    {
        node* p=&root;
        for(int j=0;j<dict[i].size();j++)
        {
            node*& pchild=p->child[dict[i][j]-'a'];
            if(pchild)
            {
                //pchild->count++;
                p=pchild;
            }
            else
            {
                pchild=new node;
                pchild->isword=false;
                //pchild->count=1;
                p=pchild;
            }
        }
        p->isword=true;
        p->count++;
    }

}
/*
int GetCount()
{
    for(int start=0;start<query)
    node* p=&root;
    for(int i=0;i<query.size();i++)
    {
        if(p->child[query[i]-'a']!=NULL)
            p=p->child[query[i]-'a'];
        else
            return 0;
    }
    return p->count;
}
*/
//bool keyindesc[n];//initaial false;
int ans;
void find(const char* query)
{
    node* p=&root;
    int stri=0;
    do
    {
        if(!query[stri])
            break;
        p=p->child[query[stri++]-'a'];
        if(p && p->isword)
        {
            p->isword=false;
            ans+=p->count;
        }
    }while(p);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            cin>>dict[i];
        CreateTrie();
        cin>>query;

        const char* cstr=query.c_str();
        for(int starti=0;starti<query.size();starti++)
        {
            find(cstr+starti);
        }
        printf("%d\n",ans);
    }

    return 0;
}

然后几天看了下瞿神的matrix store的 trie,万物都是图。同时memset(ch[0],-1, sizeof(ch[0]));

这种写法其实是-1 变成32bit1,然后初始化ch[0]指向的30个int,sizeof(ch[0])其实是不如sizeof(int)*30, 因为传入函数可能数组名退化为指针,当然一般使用全局的都没有太大的差别。

Posted by richard爱闹 - 9月 2 2014
如需转载,请注明: 本文来自 Richard

intersection of k sorted list

今天发现pop_heap的问题(后来发现似乎是MSVC里那个蛋疼的pop_heap的问题),于是再写了一遍intersection of k sorted list.

而且发现了之前那个微博童鞋代码的问题,没有考虑list里面有NULL的情况。

不过我的代码似乎稍微繁琐一些,不是那么简洁。

里面更换heap顶 然后调整还是用了pop_heap push_heap来做的,因为直接make_heap毕竟是线性时间复杂度。
由于作者的数据是倒序的,倒序是要维护min 和maxheap的,所以需要用头插法简历list,而且如果用STL的list也不好弄。

#include <cstdlib>
#include <vector>
#include <iostream>
#include <algorithm>
//#include <cstddef>
using namespace std;
struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int x):val(x),next(nullptr)
    {
    }
};
bool comp(const ListNode* p1, const ListNode* p2)
{
    return p1->val>p2->val;//greater than, min heap// less than, default, is max heap
}

vector<int> intersection(vector<ListNode*> v)
//process ascending sort, using max, minheap, check whether min of heap(top) is max, if is, then whole is equal, 
//process descending sort, using min, maxheap, check whether max of heap(top) is min, if is, then whole is equal;
{
    vector<int> intervec;
    for(int i=0;i<v.size();i++)
        if(v[i]==nullptr)
            return intervec;
    make_heap(v.begin(),v.end(),comp);//min heap of node *
    int maxv=0xFFFFFFFF;
    for(int i=0;i<v.size();i++)
        if(maxv<v[i]->val)
            maxv=v[i]->val;

    while(true)
    {
        if(v.front()->val==maxv)
        {
            intervec.push_back(maxv);
            //bool hasnull=false;
            int i;
            for(i=0;i<v.size();i++)
            {
                if(v[i]->next==NULL)
                {
                    //hasnull=true;
                    break;
                }
                else
                    v[i]=v[i]->next;
            }
            if(i<v.size())
                break;
        }
        else
        {
            if(v.front()->next==nullptr)
            {
                break;
            }
            v.front()=v.front()->next;
            maxv=max(maxv,v.front()->val);
            //swap(v.front(),v.back());
            //v.resize(v.size()-1);
            //make_heap(v.begin(),v.end(),comp);

            pop_heap(v.begin(),v.end(),comp);
            push_heap(v.begin(),v.end(),comp);


            /*
            */

        }
    }
    return intervec;
}
ListNode* InsertHead(int a[], int n)// list::push_front()
{
    if(n==0) return nullptr;
    ListNode* head=new ListNode(a[0]);
    //head->val=a[0];
    for(int i=1;i<n;i++)
    {
        ListNode* p =head;
        head=new ListNode(a[i]);
        head->next=p;
    }
    return head;
}

int main()
{
    int a[]={111,99,19,18,10,9,8,6,5,4,3,2,1,-1,-2,  
                    111,99,19,11,10,9,7,5,4,3,2,1,0,-1,-3,  
                        99,21,20,19,18,17,16,10,8,6,4,2,1,0,-1,
                            112,111,99,97,96,95,90,30,29,28,27,23,20,-1,-2,
                                110,110,99,  87,86,85,  80,80,79,  78,77,73,  71,71,62,
                                    1110,1110,188,187,186,185,180,180,179,178,177,111,99,1,-1};
    int n=sizeof(a)/sizeof(int);
    int pern=15;

    /*
    list<int> sortedlist[n/pern];
    for(int i=0;i<n;i++)
    {
        sortedlist[i/pern].push_front(a[i]);
    }
    */

    vector<ListNode* > v;
    for(int i=0;i<n/pern;i++)
    {
        v.push_back(InsertHead(a+pern*i,pern));
    }
    vector<int> intervec=intersection(v);

    for(int i=0;i<intervec.size();i++)
        printf("%d ",intervec[i]);
    return 0;
}

Posted by richard爱闹 - 9月 1 2014
如需转载,请注明: 本文来自 Richard

sort big data

或许是巧合,sorting是我们队名,下午的题目也是sorting题目,但是我们当时做的并不好,个人觉得。

首先思路比较有限,没有开阔,尽管分治在理论上和直接sort似乎没有太多的差别

直接排序nlogn
分治排序(假设刚好整分) n/m log n/m m +n= n(1+logn/m)

正如胡瀚森大神所说,log 50W~=19 和log 5W +1~=17 差别几乎是可以忽略的, 所以用分支并不会带来较明显的性能优势,自己实践发现的确也是如此。

这里实现了几个版本

  1. 直接sort,快排,也是较快的

  2. unique可以调用系统的,不一定是unique_copy,或者自己写一个one-pass的,自己写了一个双while,后来发现其实是可以缩掉一个while,正如fawkes大神所说,他习惯判断是否和前一个元素
    相等,我的是和之前已经处理过的最后一个元素是否相等。

  3. 华师软院likexi同学组提到的分治我自己写了一下,结果发现了自己对于pop_heap api使用的一个严重错误。其实本不需要用heap,但是这样也好,多暴露一些问题。
    因为分别是按照极短排序直接append就可以了,不用merge。

pop_heap 我以为是: swap(a[0],a[size-1]), size—; Filterdown(0), 但是发现不是的,a[0]也要参与比较,这样我已开始a[0] 越界了调用pop_heap 就会出bug.

pop_heap 的 Preconditions 是三个条件,[first, last)至少一个元素,也即last>first, 并且[first, last] is a heap, 所以我的做法是错误的。因为first已经越界了。

所以我目前想到的做法就是 手动swap size— 然后make_heap。 群里某童鞋发给我STL源码,之前一直以为这种自己是接触不到的,现在也要逐渐开始懂源码了。

但是后来看了下,发现其实还是要照原来的思路来理解,遇到问题不是pop_heap的问题,而是MSVC那个蛋疼的STL,那个蛋疼的algorithm里面的pop_heap, 管他怎么实现,反正和SGI STL不一样就对了。但是SGI STL里面要求原始的[first, last)是heap,这里也有异或

  1. 如果直接merge 复杂度一般是比heap要高的。

附上搞的代码,不要用MSVC编译。。。。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <ctime>
#include <vector>
using namespace std;
typedef unsigned int ui;
const ui MAXNUM=5e5;

// 实现基本的,分支的,首先unique_copy, 计时,还有多线程,Java Bigint,处理不止10位的整数,string char*, bitset, bitmap, 位率
ui a[MAXNUM];
ui b[MAXNUM];
ui n;
ui bucket_num=32;
ui MAXUINT=0xFFFFFFFF;
clock_t start;
clock_t endc;
vector<ui> bucket[MAXNUM];
vector<ui> veca;

vector<pair<int,int> > bucketpos;
void removerepeated_fawks(vector<ui>& a)
{
    int save = 0;
    int n = a.size();
    for (int i = 0; i < n; i++)
    {
        if (!i || a[i] != a[i - 1]) a[save++] = a[i];
    }
    a.resize(save);
}
void removerepeated(vector<ui>& a)
{

    if(a.size()==0) return ;
    ui bucketn = a.size();
    ui i=1,curi=0;
    while(i<bucketn)
    {
        while(i<bucketn && a[i]==a[curi])
            i++;
        if(i==bucketn) break;
        a[++curi]=a[i++];
    }
    a.resize(curi+1);
}
void removerepeated_onewhile(vector<ui>& a)
{
    if(a.size()==0) return;
    ui i=1,curi=0;

    while(i<a.size())
    {
        if(a[curi]!=a[i])
        {
            a[++curi]=a[i++];
        }
        else
            i++;
    }
    a.resize(curi+1);
}
void sort_unique_copy()
{
    sort(a,a+n);
    //ui* end_b=unique_copy(a,a+n,b);
    ui* aend=unique(a,a+n);
    //printf("%d\n",end_b-b);
    printf("%d\n",aend-a);
    for(ui* i=a;i!=aend;i++)
        printf("%u\n",*i);

    /*
    for(size_t i=0;i<n;i++)
        printf("%d ",b[i]);
    */
    puts("");
}

void sort_one_pass()
{
    //start=clock();
    sort(veca.begin(),veca.end());
    //endc=clock();
    //printf("STL Sorting Time: %f\n",(double)(endc - start) / CLOCKS_PER_SEC);
    removerepeated_fawks(veca);
    /*
    ui i=1,curi=0;
    while(i<n)
    {
        while(i<n && a[i]==a[curi])
            i++;
        if(i==n) break;
        a[++curi]=a[i++];
    }*/
    //start=clock();
    printf("%d\n",veca.size());
    for(ui i=0;i<veca.size();i++)
        printf("%u\n",veca[i]);
    //endc=clock();
    //printf("Writing Time: %f\n",(double)(endc - start) / CLOCKS_PER_SEC);
}

bool AtLeastTwoArray()
{
    if(bucketpos.size()>=2)
        return true;
    else
        return false;
}

bool comp(const pair<int,int> i, const pair<int,int> j)
{
    return bucket[i.first][i.second]>bucket[j.first][j.second];
}

void sort_m_bucket_append()
{
    bucketpos.clear();
    //ui threshold[bucket_num];
    //for(ui i=0;i<bucket_num;i++)
    //    threshold[i]=n/4*i;
    //ui merge[n];
    for(ui i=0;i<bucket_num;i++)
        bucket[i].clear();
    ui bucket_cap=MAXUINT/bucket_num+1;
    for(ui i=0;i<n;i++)
    {
        bucket[veca[i]/bucket_cap].push_back(veca[i]);
    }

    for(ui i=0;i<bucket_num;i++)
    {
        sort(bucket[i].begin(),bucket[i].end());
        removerepeated(bucket[i]);
    }
    ui ai=0;
    for(ui i=0;i<bucket_num;i++)
    {
        for(ui j=0;j<bucket[i].size();j++)
            a[ai++]=bucket[i][j];
    }
    printf("%u\n",ai);
    for(ui i=0;i<ai;i++)
        printf("%u\n",a[i]);
}

void sort_m_bucket()
{
    bucketpos.clear();
    //ui threshold[bucket_num];
    //for(ui i=0;i<bucket_num;i++)
    //    threshold[i]=n/4*i;
    //ui merge[n];
    for(ui i=0;i<bucket_num;i++)
        bucket[i].clear();
    ui bucket_cap=MAXUINT/bucket_num+1;
    for(ui i=0;i<n;i++)
    {
        bucket[veca[i]/bucket_cap].push_back(veca[i]);
    }

    for(ui i=0;i<bucket_num;i++)
    {
        sort(bucket[i].begin(),bucket[i].end());
        removerepeated(bucket[i]);
    }

    //vector<ui> pos(bucket_num,0);
    for(ui i=0;i<bucket_num;i++)
    {
        if(bucket[i].size()>=1)
            bucketpos.push_back(make_pair(i,0));
    }
    make_heap(bucketpos.begin(),bucketpos.end(),comp);
    ui min,ai=0;
    ui mini;
    while(AtLeastTwoArray())
    {
        a[ai++]=bucket[bucketpos.front().first][bucketpos.front().second];



        bucketpos.front().second=bucketpos.front().second+1;
        if(bucketpos.front().second>=bucket[bucketpos.front().first].size())
        {
            pop_heap(bucketpos.begin(),bucketpos.end(),comp);
            bucketpos.pop_back();
        }
        else
        {
            pop_heap(bucketpos.begin(),bucketpos.end(),comp);
            push_heap(bucketpos.begin(),bucketpos.end(),comp);
        }

        /*
        if(bucketpos.front().second+1<bucket[bucketpos.front().first].size())
        {
            bucketpos.front().second=bucketpos.front().second+1;
            pop_heap(bucketpos.begin(),bucketpos.end(),comp);
            push_heap(bucketpos.begin(),bucketpos.end(),comp);
        }
        else
        {
            swap(bucketpos.front(),bucketpos.back());
            bucketpos.pop_back();
            make_heap(bucketpos.begin(),bucketpos.end(),comp);
        }
        */
        /*
        min=0xFFFFFFFF;
        for(ui i=0;i<bucket_num;i++)
        {
            if(pos[i]>=bucket[i].size())
                continue;
            if(min>bucket[i][pos[i]])
                min=bucket[i][pos[i]],mini=i;
        }
        a[ai++]=min;
        pos[mini]++;*/
    }
    if(bucketpos.size()==1)
    {
        for(ui j=bucketpos[0].second;j<bucket[bucketpos[0].first].size();j++)
            a[ai++]=bucket[bucketpos[0].first][j];
    }


    //merged ai number of type ui into a[]
    printf("%u\n",ai);
    for(ui i=0;i<ai;i++)
        printf("%u\n",a[i]);

    /*
    for(ui i=0;i<bucket_num;i++)
    {
        if(i!=0)
            sort(a+(n/bucket_num)*i+1,(n/bucket_num)*(i+1)+2)
        else
            sort(a,a+n/bucket_num+1);
    }*/

    //sort(a,a+n/bucket_num+1);
    //sort(a+n/bucket_num+1,a+n/bucket_num)
}

int main()
{
    freopen("in.txt","r",stdin);
    freopen("out_unique_copy.txt","w",stdout);

    /*
    srand(time(NULL));
    printf("%d\n",MAXNUM);
    for(ui i=0;i<MAXNUM;i++)
        printf("%u\n",ui((double)rand()/RAND_MAX * MAXUINT));
    */
    start=clock();
    scanf("%d", &n);
    ui x;
    for(ui i=0;i<n;i++)
        scanf("%u", &x),veca.push_back(x);
    endc=clock();
    printf("Reading Time: %f\n",(double)(endc - start)/CLOCKS_PER_SEC);
    start=clock();
    sort_m_bucket();
    endc=clock();
    printf("STL Sorting Time: %f\n",(double)(endc - start) / CLOCKS_PER_SEC);
    //duration = (double)(finish - start) / CLOCKS_PER_SEC
    //printf("Reading Time: %f\n",(double)(end - start) / CLOCKS_PER_SEC);

    return 0;
}

Posted by richard爱闹 - 9月 1 2014
如需转载,请注明: 本文来自 Richard

EMC campus

unique_copy居然不知道,还是看了别人的,不过这个和自己裸写one-pass应该会差很多。

然后排序大数据,居然没有想到分支的思想,实在有点惭愧啊。就是先一遍将其分区,然后分别排序,最后
merge理论上来说会快很多的。

胡瀚森大神提到用java的treeset,似乎还没用过,只知道Collections.sort。这个算法可以去重复的同时排序,非常好用。

还记得某华师软院同学说处理大整数还可以用数据库的位率,好像不太熟悉这个概念,以为只有string可以。

http://blog.csdn.net/kaiwii/article/details/7990261

另外还有群里提到的set_intersection 可以处理集合的交集,同时返回集合的并集。

rand()函数似乎返回0-RAND_MAX, 这是lib一个常量,一般编译器里都是32767,致使随机数的范围比较局限,因此将其扩展范围需要用

(int)((double)rand()/RAND_MAX)*N)) 则可以扩成为0-N之间的任何数。

scanf 参数列表里 %d,表示signed decimal %u 表示 unsigned decimal,输入的时候%f 表示float,%lf表示double, 但是printf的时候%f 表示这两个。因为
printf里面没有lf这个参数列表

今天写的代码出现了几个bug

removerepeated算法思想上没有bug,但是用vector处理最后要resize,vector在处理数组删除的时候一般不用erase函数,效率太低。

然后还有多路merge,一定要记住最后要处理多出的一路没有merge的数组。

另外还有一点要注意,vector的front()和back() 都是return reference类型,这样那位微博童鞋的代码才可以vec.front()=vec.front->next; 假设vector vec;
当然都可以用vec[0] vec[vec.size()-1]代替

其实删除堆顶,然后插入一个元素,就是直接pop_heap push_heap来实现的,因为pop_heap 会留着最后一个元素,

今天看到一个学妹写的C++11的auto,之前一直只知道局部自动变量的缺省关键字,现在可以作为一种系统可以自动识别变量类型的关键字,在遍历容器的时候最方便,因为这种一般
只在循环里面迭代器用到,然后不用去写一个那么长的东西,直接auto就可以了,其他地方如果变量类型比较长都可以的。类似于JS里面的var

应学妹要求,今天又回顾了下虚函数,记得fawkes大神总是强调Java所有方法默认都是虚函数。

虚函数就是默认会继承给派生类的函数,如果有重载的话就用自己重新定义的,没有就调用父类的。这还可以保证如果用指针的话也不会出错,百科里有个比较好的例子

Posted by richard爱闹 - 8月 28 2014
如需转载,请注明: 本文来自 Richard

LCA based on array store

之前看过剑指Offer分析LCA问题,对于各种情况都要考虑。

这次是顺序存储的完全二叉树,计算i j结点的最短路径,其实也是LCA的应用。i j二进制位运算的共同前缀就是结果了,结点是1-based的index,
x^y得到前缀部分都是0,前缀是上面公共的部分,上限(x^y)=z,得到后面不相同的部分的位数,然后将x>>z就得到x y的LCA,如果两个节点不是祖孙关系的话。

然后处理一下,计算得到LCA和x y的分别距离加起来。

如果是祖孙关系,就是直接计算。

后来发现不知道怎么判断是否是祖孙关系,后来看了Ahdoc大神代码,发现还是可以统一到一起处理。还是要和之前两个链表的首个公共节点一样处理。
要先把两个推到同一层,然后再计算LCA,最后的距离是log2(x)+log2(y)-2*log2(z), z是x y的LCA,这样可以将两种情况统一到一起处理。

今天看到群里讨论placement new 的语法,也就是先申请一块空间,然后定位new的方式,将指针指向这块内存,但是似乎最后destructor的时候
不会释放这块空间。
http://www.cnblogs.com/weiweiqiao99/archive/2012/06/16/2545710.html

今天看到JulyEdu又有人问,我又写了一遍,这一次似乎理解更进一步了。
这道题目的递归算法是经典算法,但是我第一遍看的时候始终不得其解,今天记录下一些理解后的心得。

时间O(N), 空间O(1)似乎面试都不算系统栈空间
struct Node
{
    int val;
    Node* left, *right;
};
Node* LCA(Node*root, Node* p1, Node*p2)
{// p1&&p2, p1!=p2, p1 p2 in tree
    if(!root) return NULL;
    if(root==p1 || root==p2) return root;
    Node* leftLCA=LCA(root->left, p1, p2);
    Node* rightLCA=LCA(root->right, p1, p2);
    if(leftLCA && rightLCA) return root;
    else if(leftLCA && !rightLCA) return leftLCA;
    else if(rightLCA && !leftLCA) return rightLCA;
    else return NULL;
}

他的递归结构是指,对于左子树去找到p1或者p2, 右子树也是如此,第一次遇到其中一个节点就返回,如果第一次左右子树 分别都能找到p1 或者p2,
那么必然p1 p2 分别在左右子树,则此时root就是LCA(因为不可能左右子树同时找到p1或者p2),如果一颗子树找到,说明结果就在一颗子树那里,
如果都没有的话,说明找不到LCA,可能root空,或者p1 p2有空的,等等。

Posted by richard爱闹 - 8月 25 2014
如需转载,请注明: 本文来自 Richard

heap stl

今天重新写了求多个list的代码,感觉代码还是naive。先分析下make_heap, push_heap pop_heap的问题吧。

堆得典型应用是求最值时时间复杂度的优化,另外还有优先队列。
在群里问,大家都没有深入分析里面的实质,这样的态度其实是不好的。有的说,面试还不是重新写一遍heap,但是那是专门考heap的时候,
如果这个是你的代码的一部分,当然是用stl了,你还重新写一个么。

make_heap()是调整整个为堆,从n/2-1->0 逐个FilterDown(),默认是lower,对应的是大顶堆,先暂且记住吧。
pop_heap()这样的:如果是vector的话,swap(p[0],p[size-1]), 然后将size-1个元素调整为堆,所以后面一般还加一个pop_heap(), 具体的调整就是FilterDown(0),
pop_heap(v.begin(),v.end(),greater());
操作完后,v.back()就不属于堆了。

push_heap()这样的:如果还是vector,将v[size-1]向上调整为堆,Filterup(size-1), 也是O(logn)的复杂度。所以通常先push_back。

因此这个代码就变成这样(某微博童鞋的代码,用了其他部分沈略,用了很多C++11的语法)

vector<int> intersect(vector<Node*>& v){
    vector<int> ret;
    int maxv=(*max_element(v.begin(),v.end(),cmp))->x;
    make_heap(v.begin(),v.end(),cmp2);
    while(true){
        if(v.front()->x==maxv)
            ret.push_back(maxv);
        if(v.front()->next==nullptr)
            break;
        v.front()=v.front()->next;
        maxv=max(maxv,v.front()->x);
        pop_heap(v.begin(),v.end(),cmp2);
        push_heap(v.begin(),v.end(),cmp2);
    }
    return ret;
}

期间显示修改了堆顶为next指针,然后pop_heap 将swap(v[0],v[size-1]), 然后Filterdown(0),这是size已经-1了。
后面push_heap又将之前换到最后的加进来,通过Filterup(size-1), 之前size++了。

有的童鞋可能问,为啥不直接Filterdown(0)呢,因为堆顶换了元素后,直接调整不能保证堆的性质,必须将之前堆的尾放到堆顶Filterdown来调整,具体参照算导。

另外自己写个linklist确实naive,这时尾插法createlinklist用STL list来push_back是非常方便的,第一次和后面的push已经在里面区分好了。

另外今天还回想起遇到的greater, 这样一个函数对象,调用时要greater(), 自己定义的不能加(), 对应的是函数对象和函数指针,函数对象多数都是系统的这些,greater,less, 默认less.所以函数对象要加括号,因为是一个class的instance,函数指针则不能 加().

看了这么多,觉得自己有必要写一个heap,于是下午利用闲情逸致写了一个heap,其中heapsort是假象大顶堆,因此有问题,其他部分都是当初小顶堆来实现的。

//0-based
//min-heap
#include <cstdio>
#include <utility>
#include <algorithm>
using namespace std;
int n,minx;//heap size
int a[100000];
int left(int i){
    if(2*i+1<n)
        return 2*i+1;
    else
        return -1;
}
int right(int i){
    if(2*i+2<n)
        return 2*i+2;
    else
        return -1;
}
void FilterDown(int i);
void FilterUp(int i);
void heap_sort()//assume max heap
{
    int times=n;
    for(int i=0;i<times-1;i++)//n-1 max in the right place will finish
    {
        swap(a[0],a[n-1]);
        n--;
        FilterDown(0);
    }
}
void push_heap(int x)
{
    n++;
    a[n-1]=x;
    FilterUp(n-1);
}
void pop_heap()
{
    swap(a[0],a[n-1]);
    n--;
    FilterDown(0);
}


void make_heap()
{
    for(int i=(n-1-1)/2;i>=0;i--)
        FilterDown(i);
}
void FilterUp(int i)
{
    while(i>0)
    {
        if(a[i]>=a[(i-1)/2])
            break;
        else
        {
            swap(a[i],a[(i-1)/2]);
            i=(i-1)/2;
        }
    }
}
void FilterDown(int i)
{
    while(i<n)
    {
        if(left(i)!=-1 && right(i)!=-1)
        {
            minx=min(a[left(i)],a[right(i)]);
            if(a[i]<=minx)
            {
                break;
            }
            else
            {
                if(a[left(i)]==minx)
                {
                    swap(a[left(i)],a[i]);
                    i=left(i);
                }
                else
                {
                    swap(a[right(i)],a[i]);
                    i=right(i);
                }
            }
        }
        else if(left(i)!=-1)
        {
            if(a[i]<=a[left(i)])
                break;
            else
            {
                swap(a[i],a[left(i)]);
                break;
            }
        }
        else
            break;

    }
}
void show()
{
    for(int i=0;i<n;i++)
        printf("%d ",a[i]);
    puts("");
}

int main()
{
    freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    make_heap();
    show();
    pop_heap();
    show();
    push_heap(100);
    show();
    return 0;
}

Posted by richard爱闹 - 8月 25 2014
如需转载,请注明: 本文来自 Richard