首页 > 试题广场 >

重复词

[编程题]重复词
  • 热度指数:1901 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解

对于两个字符串B和C,我们定义BC为将C接在B的后面形成的新串。一个字符串P是串A的前缀,当且仅当存在B使得A=PB,当然B可以为空串。若P!=A,则我们称P为A的真前缀。现在定义重复词。串Q是串A的重复词当且仅当Q是A的真前缀,且A是QQ的前缀。而A的最长重复词则是A的重复词中最长的一个,或者空串(当A没有任何重复串时)。如ababab的最长重复词是abab;abc的最长重复词是空串。

给定一个串s(由字母组成),及它的长度n(1≤n≤100000),请返回s的所有前缀的最长重复词的长度之和(空串长度为0)。

测试样例:
8,"babababa"
返回:24
 

import java.util.*;

/*
 * 简单做一下说明,根据题意我们知道,重复词相当于将字符串拆分,并且后面部分是前面部分的前缀,
 *
 * 才能构成重复词 比如: a b c d a b 就可以切分为  a b c d | a b 
 *
 *             a b c d | a b c d
 *
 * 不难看出,任何一个重复词的形成,一定以字符串首字符进行划分的,比如上个例子中的a,如果划分线 |之后的是其他字符,
 *
 *  如 a b c d a | b
 *
 *   a b c d a | a b c d a 前缀的匹配一定是失败的。
 * 
 *  那么这样就有了一个思路,遍历一次字符串,找出所有出现首字符的位置(不包括第一个字符本身),
 * 
 *  因为划分线靠后,得到的重复词也就越长,所以我选择的是从后向前遍历。
 * 
 *  对于每一个出现首字符的位置 i,从第一个字符到第 i-1 个字符,
 * 
 *  就是从第一个字符到第 i 个字符的重复词,
 * 
 *  比如还是刚才的例子 a b c d a b这个字符串的前缀 a b c d a 可以划分为 a b c d | a 划分线前的部分就是这个字符串的重复词
 * 
 *  这样我们知道 原字符串 a b c d a b 的一个前缀 a b c d a 的最大重复词长度为4,把4记录下来,(我是放在了数组里)
 * 
 *  同理,继续比较 i 的下一个字符与第二个字符是否相等,如果相等,说明从第一个字符到第 i+1个字符也能与之前的重复词匹配 ...
 * 
 *  依次类推 直到某一个字符匹配失败了,那么后续都不能匹配了,直接跳出循环(对应字符串的重复词相当于未找到)

 * 但是匹配一次未找到是不是意味着该字符串就没有重复词存在呢,这也未必,看下面例子
 *
 * a b c d g... a b c d e f  a b c d g...a b c d e f ,
 *
 * 以最后一个a为标准时,后面的b c d 都可以匹配成功,但是 e,f没有匹配成功,第一次就为0,
 *
 * 然后以倒数第二个a 为标准时,e ,f 才匹配成功,所以这是个不断更新的过程 ,
 *
 * 对于任何一个字符m,以第一个字符开头,m 结尾的字符串如果存在重复词,那么必然存在一个与首字符相同的a ,
 *
 * 使得从首字符和a开始匹配,能够一直匹配到m,这就保证了更新的完备性
 *
 * 更新时 如果填入的数比原来的数大就填,否则忽略
 *
 * 最后把结果加起来就行了
 * */

public class Periods {
 
 public long getLongest(int n,String s){
  
  if(n==1){return 0;}
  
  char[] list = s.toCharArray();
  
  ArrayList<Integer> capital= new ArrayList<Integer>();
  
  for(int i=n-1;i>0;i--){
   
   if(list[i] == list[0]){
    
    capital.add(i);
    
   } 
  }
  
  /*
  for(int i=0;i<capital.size();i++){
   
   System.out.println("capital["+i+"] = "+capital.get(i));
   
   
  }*/
  
  long sum =0;
  
  int[] arr = new int[n];
  
  for(int j=0;j<capital.size();j++){

    for(int i=capital.get(j),k=0;i<n;i++,k++){
     
     if(list[k]==list[i]){
      
      arr[i] = (arr[i]>capital.get(j))?arr[i]:capital.get(j);
       
     }
     else{
      
      break;
     }
    }
    /*
    for(int i=0;i<n;i++){
     
     System.out.println("arr["+i+"]= "+arr[i]);
    }
    */
 
  }
  
  
  for(int i=0;i<n;i++){
   
   sum+=arr[i];
  }
  
  return sum;

  }
 
}
发表于 2016-07-09 17:54:26 回复(6)
class Periods {
public:
    long long getLongest(int n, string s) {
        long long r = 0;
        if(s.length() == 0)
            return r;
        const char *p = s.c_str();
        bool flag[100005];
        
        memset(flag, false, sizeof(flag));
        int lastIndex = n;
        int count = 1;
        for(int i=n-1;i>=1;i--)
        {
            count = 1;
            if(p[i] == p[0])
            {
                r += i;
                int tmp = 1;
                int k = i+1;
                while(k<n && tmp<i)
                {
                    if(p[tmp] == p[k])
                    {
                        if(p[k]!=p[0] && !flag[k])
                        {
                            flag[k] = true;
                            r += i;                         }                         tmp++;                         k++;                     }                     if(p[tmp] != p[k])                         break;                 }             }         }         return r;
    }
};


发表于 2017-12-03 09:52:21 回复(1)

  简单做一下说明,根据题意我们知道,重复词相当于将字符串拆分,并且后面部分是前面部分的前缀,才能构成重复词 比如: a b c d a b 就可以切分为  a b c d | a b  
        a b c d | a b c d
 *
 * 不难看出,任何一个重复词的形成,一定以字符串首字符进行划分的,比如上个例子中的a,如果划分线 |之后的是其他字符,
 *
 *  如 a b c d a | b
 *
 *   a b c d a | a b c d a 前缀的匹配一定是失败的。
 * 
 *  那么这样就有了一个思路,遍历一次字符串,找出所有出现首字符的位置(不包括第一个字符本身),
 * 
 *  因为划分线靠后,得到的重复词也就越长,所以我选择的是从后向前遍历。
 * 
 *  对于每一个出现首字符的位置 i,从第一个字符到第 i-1 个字符,
 * 
 *  就是从第一个字符到第 i 个字符的重复词,
 * 
 *  比如还是刚才的例子 a b c d a b这个字符串的前缀 a b c d a 可以划分为 a b c d | a 划分线前的部分就是这个字符串的重复词
 * 
 *  这样我们知道 原字符串 a b c d a b 的一个前缀 a b c d a 的最大重复词长度为4,把4记录下来,(我是放在了数组里)
 * 
 *  同理,继续比较 i 的下一个字符与第二个字符是否相等,如果相等,说明从第一个字符到第 i+1个字符也能与之前的重复词匹配 ...
 * 
 *  依次类推 直到某一个字符匹配失败了,那么后续都不能匹配了,直接跳出循环(对应字符串的重复词相当于未找到)

 * 但是匹配一次未找到是不是意味着该字符串就没有重复词存在呢,这也未必,看下面例子
 *
 * a b c d g... a b c d e f  a b c d g...a b c d e f ,
 *
 * 以最后一个a为标准时,后面的b c d 都可以匹配成功,但是 e,f没有匹配成功,第一次就为0,
 *
 * 然后以倒数第二个a 为标准时,e ,f 才匹配成功,所以这是个不断更新的过程 ,
 *
 * 对于任何一个字符m,以第一个字符开头,m 结尾的字符串如果存在重复词,那么必然存在一个与首字符相同的a ,
 *
 * 使得从首字符和a开始匹配,能够一直匹配到m,这就保证了更新的完备性
 *
 * 更新时 如果填入的数比原来的数大就填,否则忽略
 *
 * 最后把结果加起来就行了
import java.util.*;

public class Periods {
    public long getLongest(int n, String s) {
        
        ArrayList<Integer> arr=new ArrayList<>();
        for(int i=n-1; i>0; i--){
            if(s.charAt(i)==s.charAt(0))
                arr.add(i);
        }
        int sum=0;
        int[] b=new int[n];
        for(int i=0; i<arr.size(); i++){
            for(int j=arr.get(i),k=0; j<n; j++,k++){
                if(s.charAt(j)==s.charAt(k))
                    b[j]=(b[j]>arr.get(i))? b[j]:arr.get(i);
                else
                    break;
            }
        }
        for(int ele: b)
            sum+=ele;
        return sum;
    }
}

发表于 2018-06-27 09:54:41 回复(0)
Python通过
# -*- coding:utf-8 -*-
import collections
class Periods:
    def getLongest(self, n, s):
        ans = [0 for i in range(n)]
        start_c = s[0]
        start_index = []
        for i in range(1,n):
            if s[i]==start_c:
                start_index.append(i)
        for index in start_index[::-1]:
            ans[index] = index
            ans_temp = index
            temp = 1
            index+=1
            while index<n and (temp+1)*2<=(index+1) and s[index]==s[temp]:
                ans[index] = max(ans[index],ans_temp)
                index+=1
                temp+=1
        return sum(ans)
发表于 2018-06-29 11:11:45 回复(0)
classPeriods {
public:
    longlonggetLongest(intn, string s) {
        // write code here
        longlong  res = 0;
        if(s.length() == 0) returnres;
        constchar*p = s.c_str();
        bool flag[100005];
        memset(flag, false, sizeof(flag));
        intlastindex = n;
        intcoun = 1;
        for(inti = n -1; i >= 1; i--)
        {
            coun = 1;
 
            if(p[i] == p[0])
            {
               
                    res+=i;
                    inttmp= 1;
                 
                    intk = i+1;
                 
                    while(k<n && tmp <i)
                    {
 
                        if(p[tmp]== p[k])
                        {
                          
                            if( p[k] != p[0] && !flag[k] )
                            {
                                flag[k] = true;
                             
                                res+= i;
                            }
                            tmp++;k++;
                        }
                        if(p[tmp]!= p[k])
                            break;
                    }
                   // lastindex = i;
                   // res += coun*i;
 
 
            }
        }
        returnres;
 
    }
发表于 2016-05-02 11:03:14 回复(0)
classPeriods {
public:
    longlonggetLongest(intn, string s) {
        // write code here
        longlong  res = 0;
        if(s.length() == 0) returnres;
        constchar*p = s.c_str();
        bool flag[100005];
        memset(flag, false, sizeof(flag));
        intlastindex = n;
        intcoun = 1;
        for(inti = n -1; i >= 1; i--)
        {
            coun = 1;
 
            if(p[i] == p[0])
            {
               
                    res+=i;
                    inttmp= 1;
                 
                    intk = i+1;
                 
                    while(k<n && tmp <i)
                    {
 
                        if(p[tmp]== p[k])
                        {
                          
                            if( p[k] != p[0] && !flag[k] )
                            {
                                flag[k] = true;
                             
                                res+= i;
                            }
                            tmp++;k++;
                        }
                        if(p[tmp]!= p[k])
                            break;
                    }
                   // lastindex = i;
                   // res += coun*i;
 
 
            }
        }
        returnres;
 
    }
};

发表于 2016-01-18 16:40:18 回复(1)
#include<string>
class Periods {
public:
	long long getLongest(int n, string s) {
		// write code here
		long long	res = 0;

		//1求所有前缀
		for (int i = 0; i <= n; i++)
		{
			
			string subS = s.substr(0, i);		//前缀

			bool get = false;	//是否获得最长重复词

			//判断是否是重复词。//要求之一是qq 有前缀A所以长度限制
			for (int k = i-1; k >= i / 2; k--)
			{
				//真前缀  i-1 保证
				string subsubS = subS.substr(0, k);
				
				//前缀
				string supS = subsubS + subsubS;
				string tmp = supS.substr(0, subS.length());
				if (subS == tmp)
				{
					get = true;
					cout << subS << ":::::" << "\t";        //输出测试
					cout << subsubS << endl;                //输出测试
					res += subsubS.length();
				}
				else if (get == true)break;
			}
		}
		return res;
	}
};
8,"babababa"
返回:24
测试结果
bab:::::        ba
baba:::::       ba
babab:::::      baba
bababa:::::     baba
bababab:::::    bababa
babababa:::::   bababa
24请按任意键继续. . .
发表于 2016-01-16 23:20:22 回复(2)

问题信息

难度:
7条回答 7917浏览

热门推荐

通过挑战的用户

查看代码