字符串的最长连接

看到的一道面试题:有 n 个长为 m+1 的字符串,如果某个字符串的最后 m 个字符与某个字符串的前 m 个字符匹配,则两个字符串可以联接, 问这 n 个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。

根据网友的思路,可以用拓扑排序来求解,抽空实现了一次,大致如下。

首先转化为图论问题,每个字符串抽象为1个节点,如果2个字符串能联接则表示一条有向边。问题转换为找到最长的一条路径,并且检查是否存在环(回路)。
寻找路径的方式类似拓扑排序,从入度为0的节点开始,访问它的所有联通节点,访问节点的时候顺带记录当前的最长路径,当节点的访问次数的等于入度的时候(相当于入度被减为0)则可以从这个节点开始后续的遍历。
在所有入度为0的节点遍历结束后,如果存在节点的访问次数小于入度(即入度没有被减为0),则代表出现了环。
另外,在构建图的连接表的时候需要比较字符串,因为无聊,下面将字符串映射为唯一的md5键值,用键值来代替字符串的比较。

#include <windows.h> 
typedef struct { 
    unsigned long i[2];  
    unsigned long buf[4];  
    unsigned char in[64]; 
    unsigned char digest[16];
} MD5_CTX;
//声明函数指针的时候必须加上winapi指示,否则运行会出错
typedef void (WINAPI* PMd5Init) (MD5_CTX*);
typedef void (WINAPI* PMd5Update) (MD5_CTX*, unsigned char*, unsigned int);
typedef void (WINAPI* PMd5Final) (MD5_CTX*);
void calcMd5(char* s, int len,MD5_CTX* md5)
{
    HINSTANCE hDll = LoadLibrary(L"Cryptdll.dll");
    if(hDll != NULL)
    {
        PMd5Init md5Init = (PMd5Init)GetProcAddress(hDll,"MD5Init");
        PMd5Update md5Update = (PMd5Update)GetProcAddress(hDll,"MD5Update");
        PMd5Final md5Final = (PMd5Final)GetProcAddress(hDll,"MD5Final");
        
        md5Init(md5);
        md5Update(md5,(unsigned char*)s,len);
        md5Final(md5);
    }
}

//保存每个节点的访问信息
struct VISIT_INFO{
    int visitTime; //访问次数
    int inDegree;   //节点的入度
    vector<int> path; //最大的路径
};

//从节点head开始遍历图G,visitInfo中记录了每个节点的访问次数、入度、当前最长路径
void doTopoSort(int head,const vector<vector<int> >& G, vector<VISIT_INFO>& visitInfo)
{
    visitInfo[head].path.push_back(head);

    int nodeCnt = G.size();
    for(int i=0; i < nodeCnt; ++i)
    {
        if(G[head][i] == 1 )
        {   
            if( visitInfo[i].path.size() < visitInfo[head].path.size())
            {
                visitInfo[i].path = visitInfo[head].path;//保存最长路径
            }
  
            visitInfo[i].visitTime++;
            //和head连通的节点i的入度被减为0,可以从i开始新一轮的递归
            if(visitInfo[i].visitTime == visitInfo[i].inDegree)
            {
                doTopoSort(i,G,visitInfo);
            }
        }
    }
}
//非递归求解,遍历图G,visitInfo中记录了每个节点的访问次数、入度、当前最长路径
void doTopoSort(const vector<vector<int> >& G, vector<VISIT_INFO>& visitInfo)
{
    std::deque<int> queue; //存放入度为0的节点
    int nodeCnt = G.size();
    for(int i=0; i < nodeCnt; ++i)
    {
        if(visitInfo[i].inDegree == 0)
        {
            queue.push_back(i);
            visitInfo[i].path.push_back(i);
        }
    }

    
    while(queue.size() > 0)
    {
        int currNode = queue.front();
        queue.pop_front();
        for(int i=0; i < nodeCnt; ++i)
        {
            if(G[currNode][i] == 1 )
            {
                if( visitInfo[i].path.size() < visitInfo[currNode].path.size())
                {
                    visitInfo[i].path =  visitInfo[currNode].path;
                }

                visitInfo[i].visitTime++;
                if(visitInfo[i].visitTime == visitInfo[i].inDegree)
                {
                    queue.push_back(i);
                    visitInfo[i].path.push_back(i);
                }
            }
        }
    }
}

//转存md5值
struct CODE{
    unsigned char digest[16];
};
//返回最长字符串的长度,第1个入参为字符串数组,第二个入参为字符串的个数
int longestString(char** s, int n)
{
    if(s == NULL){return -1;}
    
    //保存每个字符串的串首和串尾(len-1长度)对应的md5编码
    vector<vector<CODE> > stringCode(n,vector<CODE>(2));
    MD5_CTX md5;
    int len = strlen(s[0]);
    for(int i=0; i < n; ++i)
    {
        assert( len == strlen(s[i]));
        calcMd5(s[i],len-1,&md5);//计算串首的md5值
        memcpy(&stringCode[i][0],md5.digest,sizeof(CODE)) ;
        calcMd5(s[i]+1,len-1,&md5);
        memcpy(&stringCode[i][1],md5.digest,sizeof(CODE)) ;
    }

    //nxn的矩阵G用来保存字符串之间的链接关系,如果a->b可以联接,则G[a][b]==1
    vector<vector<int> > G(n, vector<int>(n,0));VISIT_INFO temp={0,0};
    vector<VISIT_INFO> visitInfo(n,temp);
    for(int i=0; i < n; ++i){
        for(int j=0; j < n; ++j)
        {
            if(memcmp(&stringCode[i][1],&stringCode[j][0],sizeof(CODE))==0
                && j!=i) //不允许出现自己和自己连接
            {
                G[i][j] = 1;
                visitInfo[j].inDegree++;
            }
        }
    }
    
    //topo排序的递归版本
    for(int i=0; i < n; ++i)
    {
        if(visitInfo[i].inDegree == 0)
        {
            doTopoSort(i,G,visitInfo);
        }
    }
    //topo排序的非递归版本
    
//doTopoSort(G,visitInfo);

    int maxLen = -1,maxNode;
    for(int i = 0; i < n; ++i)
    {
        if(visitInfo[i].visitTime < visitInfo[i].inDegree)
        {
            return -1//访问次数小于入度,出现了环
        }
        if(int(visitInfo[i].path.size()) > maxLen)
        {
            maxNode = i;
            maxLen = visitInfo[i].path.size();
        }
    }
    for(size_t i=0; i < visitInfo[maxNode].path.size(); ++i)
    {
        int idx = visitInfo[maxNode].path[i];
        printf("%s->",s[idx]);
    }
    printf("\n");

    return maxLen+len-1;
}

 

// 123--> 231--->314
//      /   \     \
//023-->     311   \
//            \     \
//             114-->142-->426-->267
//                     \ 
//                      425
static void test3()
{
    char* s[10]={"123",
                "231",
                "023",
                "314",
                "142",
                "426",
                "267",
                "311",
                "114",
                "425"};
    assert(9==longestString(s,10));
}
// 123--> 231--->314
//      /   \     \
//023-->     311   \
//  \         \     \
//   \         114-->142-->426-->267
//    \               \ 
//      <------------- 423
static void test4()
{
    char* s[10]={"123",
                "231",
                "023",
                "314",
                "142",
                "426",
                "267",
                "311",
                "114",
                "423"};
    assert(-1==longestString(s,10));
}

//       123--> 231--->314
//            /   \     \
//002-->023-->     311   \
//                  \     \
//                   114-->142-->426-->267
//                          \ 
//                           425
static void test5()
{
    char* s[11]={"123",
                "231",
                "023",
                "314",
                "142",
                "426",
                "267",
                "311",
                "114",
                "425",
                "002"};
    assert(10==longestString(s,11));
}
void testLongestString()
{
    test3();
    test4();
    test5();
}
posted @ 2015-01-04 23:51  _pop  阅读(709)  评论(0编辑  收藏  举报