关于腾讯的那道题截取字符串的题

an9ryfr09 2010-08-16 02:02:07
加精
记得是前阵子去腾讯面试时的那道题,当时用笔我没写出来,就大概说了下思路,今天有空,就写了一下,发现要做到完美还是很麻烦的。


题目是:
假设有"123<em>abc</em>456<em>def</em>789"这么一个字符串,写一个函数,可以传入一个字符串,和一个要截取的长度。返回截取后的结果。

要求:
1 <em>和</em>标记不得计算在长度之内。
2 截取后的字符串,要保留原有<em>标签,不过如果最后有一个标签没有闭合,则去掉其开始标签。

示例:
题中的字符串,要截取长度5,则返回的字符串应该为:123ab,要截取长度8,应返回123<em>abc</em>45。



我的做法大概思路是:
1 首先顺序读取字符串,并用一个resultstr变量来记录所有字符,当发现<标记时,开始将这个子字符串用tag变量记录下来。
2 如果发现tag变量形式为<\w+>也就是html标签的开始标记),就将其入栈。用栈结构来存储这个标记。若遇到<\/\w+>(html标签的结束标记),就出栈。

3 否则如果是常规字符的话,长度计数器++。直到与传入的要截取长度相等。

4 最后判断栈是否为空,如果为空,直接返回截取后的字符串,否则,将栈中剩余元素一个个出栈,循环从截取后的字符串中查找栈顶标签元素最后一次出现的位置,返回索引,将这个标签替换为空。直到整个栈为空为止。最后返回处理后的字符串。



原题其实是没有考虑标签嵌套的情况的,我尽量的让程序可以处理嵌套标签,使其更健壮。并且尽量少的去用php的内置函数,因为那样可能会掩盖算法本身。如可以处理a1<p>b2<em>c3</em>d4</p>e5
这种形式的嵌套标签。但我发现如果标签是这种不规则形式a1<p>b2<em>c3d4</p>e5的话,程序就会出问题了。因为我只将取到的结束标记与栈顶的元素相比较,而没有去循环搜索整个栈。这里要改一下其实也可以。


不知大家还有没有其他思路,我感觉我这么做实在是太麻烦了,罗罗嗦嗦一大堆。这么多代码面试时拿笔写非得疯了不可。而且这个时间复杂度不理想,大概为O(n*(n2))。空间方面也占了很多多余的空间。我相信一定有很简单的办法。希望大家一起想想有什么更简单的方法没?




<?php
function mySubstr( $str, $length ){

$tagcnt = 0;
$charcnt = 0;
$tag = '';
$maxlen = strlen( $str );
$resultstr = '';
$tagstack = array();

for( $i = 0; $i < $length; $i++ ){
if( $str[$i] == '<' ){

$resultstr .= $str[$i];

for( $j=$i; $str[$j]!='>'; $j++,$length++ ){
$tag .= $str[$j];
}
$tagcnt++;
$length++;
$tag .= '>';

//如果是开始标记,则入栈,如果是与之相对应的结束标记则出栈
if( preg_match('/<([^\/]+)?>/i', $tag, $r) ){
echo '入栈:',htmlspecialchars($r[1]),'<br />';
array_push($tagstack, $r[1]);
}
elseif( preg_match( '/'.$tagstack[count($tagstack)-1].'/', $tag ) ){
echo '出栈:',htmlspecialchars($tagstack[count($tagstack)-1]),'<br />';
array_pop( $tagstack );
}

$tag = '';
continue;
}

$charcnt++;
$resultstr .= $str[$i];
}


echo '<hr size=1>最后结果为:';

//栈是空的直接返回
if(empty($tagstack)){
return $resultstr;
}
//否则去掉没有结束标记的开始标记
else{

while(!empty($tagstack)){

$tag = array_pop($tagstack);

$index = strrpos($resultstr, $tag);

for($i = $index-1; $resultstr[$i] != '>'; $i++ ){
$resultstr[$i] = '';
}

$resultstr[$i++] = '';

}

return $resultstr;
}

}

$sttime = microtime(true);

$stmem = memory_get_usage();

$str = "a1<body>b2<p>c3<em>d4</em>e5</p>f6</body>g7h8";

echo '处理结果为:<br/><hr size=1>',htmlspecialchars( mySubstr( $str, 18 ) ),'<br />';

echo "内存使用情况:",(memory_get_usage()-$stmem),'<br />';

echo "算法运行时间(microtime):",(microtime(true)-$sttime),'<br/>';
...全文
29110 467 打赏 收藏 转发到动态 举报
写回复
用AI写文章
467 条回复
切换为时间正序
请发表友善的回复…
发表回复
裸睡的咸鱼干 2013-03-23
  • 打赏
  • 举报
回复
靠,表示完全看不懂!!!
热豆腐 2013-03-15
  • 打赏
  • 举报
回复
就题论题,一个简单的方法: <?php $str = "123<em>abc</em>456<em>def</em>789"; function cut($str, $num){ $len = strlen($str); $count = 0; $location = 0; for($i=0;$i<$len;){ if($str[$i] == "<"){ $i++; while($str[$i]!=">"){ $i++; } }else{ $count++; if($count == $num) { $location = $i+1; } } $i ++; } $newstr = substr($str,0,$location); //echo $newstr,"\n"; preg_match_all("/<\w+>/",$newstr,$matches1); preg_match_all("/<\/\w+>/",$newstr,$matches2); if(count($matches1[0]) != count($matches2[0])){ $last = array_pop($matches1[0]); //var_dump($last); } $pos = strrpos($newstr, $last); return substr($newstr,0,$pos).substr($newstr, $pos+strlen($last)); } cut($str,11); ?>
snipersheep 2012-11-12
  • 打赏
  • 举报
回复
只有学习的份。
peislove 2012-11-07
  • 打赏
  • 举报
回复
引用 15 楼 xuzuning 的回复:
写了个简单的,欢迎拍砖PHP code1234567891011121314151617181920212223242526272829303132333435363738394041/** * 函数名 html_substr * 功能 从html串中截取指定长度的字串,html标记不计算在内 * 参数 * $str 要截取的串 * $len 要截取的长度 * $m……
function subs($str,$int) { $count=0; $count_tmp=0; $arr=preg_split("/(<([\w]+)([^>]*)>)|(<\/([\w]+)([^>]*)>)/",$str,-1,PREG_SPLIT_OFFSET_CAPTURE); echo "<pre>"; print_r($arr); echo "<br/>"; for($i=0;$i<count($arr);$i++) { if($count<$int) { $count_tmp=$count+strlen($arr[$i][0]); if($count_tmp>=$int) { $tmp=(strlen($arr[$i][0])-($count_tmp-$int)); if($i%2) { $len=$arr[$i-1][1]+strlen($arr[$i-1][0]); return substr($str,0,$len).substr($arr[$i][0],0,$tmp); } else { $len=$arr[$i][1]+$tmp; return substr($str,0,$len); } } else { $count=$count_tmp; } } } return $arr; } $string='abc<em>sdfsd</em>dfsd<em>sdfsd</em>fsdfs'; $arr1=subs($string,5); echo "<pre>"; $arr2=htmlspecialchars($arr1); print_r($arr2);
劉哈哈 2012-10-05
  • 打赏
  • 举报
回复
都是大神啊。
oasisxp 2012-09-20
  • 打赏
  • 举报
回复
初学者,先学习
ws_123456 2012-08-13
  • 打赏
  • 举报
回复
干嘛要那么复杂啊这个<em>不是写死了直接用正则不就可以了?
Fiyun123 2012-07-19
  • 打赏
  • 举报
回复
...小弟也有一个好想法分享,五六行代码就可以解决了


就题论题而言,可以str_replace把<em>标签与 </em>去掉
然后就想截取几个就截取几个 strsub

然后数个数 超过7个就每6个插入标签分别插入<em>与</em>就OK了呀 然后 代码应该不会超过10行的 呵呵
LKK 2011-09-07
  • 打赏
  • 举报
回复
标记一下.
love_zhao311 2011-09-07
  • 打赏
  • 举报
回复
历害呀!
yyaccp_yy 2011-07-30
  • 打赏
  • 举报
回复
学习...
xiefeng125 2011-07-14
  • 打赏
  • 举报
回复
mark
--reply by CSDN Study V1.0.0.3 (starts_2000)
LEIIT 2011-05-20
  • 打赏
  • 举报
回复
$str="123<em>abc</em>456<em>def</em>789";
//echo strrpos('12<em>','<em>');

echo htmlspecialchars(mysubstr($str,13));

function mysubstr($str,$len=0){
$str1=str_replace('<em>','',$str);
$str1=str_replace('</em>','',$str1);
$substr=substr($str1,0,$len);
$str3=substr($substr,strlen($substr)-1,1);
$index=strpos($str,$str3);
$str4=substr($str,0,$index+1);
if(strrpos($str4,"</em>")===false || strrpos($str4,"</em>")<strrpos($str4,"<em>")){
$str5=substr($str4,0,strrpos($str4,"<em>"));
$str5.=substr($str4,strrpos($str4,"<em>")+strlen('<em>'),strlen($str4));
return $str5;
}else{
return $str4;
}

}



那个e 没有处理 会跟数学字符混乱
blue44sky 2011-03-02
  • 打赏
  • 举报
回复
上面方法的前提是,要用唯一的分隔符
blue44sky 2011-03-02
  • 打赏
  • 举报
回复
写了一个不用正则,纯php函数实现的。
function cutString($str,$len){
$str1 = str_replace(array('<em>','</em>'),array('|','@'),$str);
$splitNum = substr_count(substr($str1,0,$len),'|')+substr_count(substr($str1,0,$len),'@');
$str2 = substr($str1,0,$len+$splitNum);
$len1 = substr_count($str2,'|');
$len2 = substr_count($str2,'@');
if ($len1>$len2){
$str2 = substr($str2,0,strpos($str2,'|',$len1-1)).substr($str2,strpos($str2,'|',$len1-1)+1);
}
return str_replace(array('|','@'),array('<em>','</em>'),$str2);
}
$str = '123<em>abc</em>456<em>lsaf12</em>sdklfskdf<em>00000</em>';
echo cutString($str,5);
fanercute 2011-02-25
  • 打赏
  • 举报
回复
上面那个有点bug 修正一下

function sub_str($the_str,$len){
/**将要截取的长度与原字符串长度对应**/
$j = 0;
for($i = 0; $i < $len; $i++){
$a_char = substr($the_str,$j,1);
if($a_char == '<'){
$j = strpos($the_str,'>',$j) + 1;
$i--;
}else{
$j++;
}
}
/**对应结束**/
//截取
$get_thestr = substr($the_str,0,$j);
//判断截取后的字符串是否有<em>标签
$tip_index = strrpos($get_thestr,'<',0);
if($tip_index == false){
return $get_thestr;
}
//判断是否需要过滤标签
$anti_index = strpos($get_thestr,'>',$tip_index);
if($anti_index - $tip_index == 3){
$head_thestr = substr($get_thestr,0,$tip_index);
$last_thestr = substr($get_thestr,$anti_index + 1);
$get_thestr = $head_thestr.$last_thestr;
return $get_thestr;
}else if($anti_index - $tip_index == 4){
return $get_thestr;
}
}
fanercute 2011-02-24
  • 打赏
  • 举报
回复
如果只按题面意思处理的话,我写了个简单的

function sub_str($the_str,$len){
/**将要截取的长度与原字符串长度对应**/
$j = 0;
for($i = 0; $i < $len; $i++){
$a_char = substr($the_str,$j,1);
if($a_char == '<'){
//检查是否有类似'2<3<em>3'的特殊情况
$check_str = substr($the_str,$i + 1,strpos($the_str,'>',$j) - $i - 1);
$check_index = strpos($check_str,'<');
//出现标签
if($check_index == false){
$j = strpos($the_str,'>',$j) + 2;
}else{
$j++;
}
}else{
$j++;
}
}
/**对应结束**/
//截取
$get_thestr = substr($the_str,0,$j);
//判断截取后的字符串是否有<em>标签
$tip_index = strrpos($get_thestr,'<',0);
if($tip_index == false){
return $get_thestr;
}
//判断是否需要过滤标签
$anti_index = strpos($get_thestr,'>',$tip_index);
if($anti_index - $tip_index == 3){
$head_thestr = substr($get_thestr,0,$tip_index);
$last_thestr = substr($get_thestr,$anti_index + 1);
$get_thestr = $head_thestr.$last_thestr;
return $get_thestr;
}else if($anti_index - $tip_index == 4){
return $get_thestr;
}
}
networkwx 2010-12-15
  • 打赏
  • 举报
回复

<?php
$str = '123<em>abc<br/></em>45</em>6<em>def</em>789';
echo sub_str($str,10);

function sub_str($str,$length)
{
$len = strlen($str);
$tag_stat = 0; //0-闭合 1-打开
$stack = array();
$stack_len = 0;
$data_len = 0;
$last_tag_index = -1;
$lookup_stack = array();

for($i=0;$i<$len;$i++){
if($data_len >= $length) break;
if($str[$i] == '<'){//标签开始
$tag_stat = 1;
$stack[$stack_len] = '<';
}elseif($str[$i] == '>'){//标签结束
$tag_stat = 0;
$stack[$stack_len].= '>';
$key = $stack_len;
$tag = $stack[$key];
$stack_len++;

if(preg_match('/^<\w+[\/]>$/',$tag)){ //是自闭标签,不管他,让他自身自灭吧
continue;
}

if(preg_match('/^<\/\w+>$/',$tag)){//是闭合标签
if($last_tag_index == -1){//非闭合还没开始呢,不多说,直接删~~~
unset($stack[$key]);
continue;
}
if(str_replace('/','',$tag) == $stack[$last_tag_index]){
unset($lookup_stack[$last_tag_index]);//正常闭合,他不是非法的标签,所有从非法标签中移走
}else{
unset($stack[$key],$stack[$last_tag_index]);//不是正常的标签,移除本身并且移除与他同级的非闭合标签
}
$last_tag_index = -1;
}

if(preg_match('/^<\w+>$/',$tag)){//非闭合标签,并把他列入非法标签
$last_tag_index = $key;
$lookup_stack[$last_tag_index] = $last_tag_index;
}
}else{
if(isset($stack[$stack_len])){
$stack[$stack_len].= $str[$i];
}else{
$stack[$stack_len] = $str[$i];
}
if($tag_stat == 0){
$data_len++;
$stack_len++;
}
}
}

//清除那些不走寻常路的非法标签
foreach($lookup_stack as $key){
unset($stack[$key]);
}

return implode('',$stack);
}
?>


我也贴点我的代码。。。
doniel 2010-11-26
  • 打赏
  • 举报
回复
终于看完了。。我是忠实的测试员。。。
Bacon0612 2010-11-10
  • 打赏
  • 举报
回复
那个$length为什么要累加啊?如果累加那个循环不就是个死循环了吗?初学者不明白,呵呵,顶一下吧
加载更多回复(392)

21,886

社区成员

发帖
与我相关
我的任务
社区描述
从PHP安装配置,PHP入门,PHP基础到PHP应用
社区管理员
  • 基础编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧