罚抄一百遍

题目1 : 罚抄一百遍

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Ho忘了做英语作业,被老师罚抄某段文本N遍。抄写用的作业纸每行包含M个格子,每个格子恰好能填写一个字符或者空格。抄写过程中单词不能跨行,如果某行剩余的格子不足以写完一个单词,那么这个单词需要写在下一行。单词间的空格不能省略。

例如在M=9的作业纸上写2遍"Good good study day day up":

123456789
Good good
 study   
day day   
up Good  
good     
study day
 day up  

小Ho想知道当他抄写完N遍以后,最后一个字符在第几行、第几列。

输入

第一行包含两个整数N和M。  

第二行包含要抄写的文本。文本只包含大小字母和空格,并且单词之间只有一个空格。

对于40%的数据,1 <= N, M <= 1000  

对于100%的数据,1 <= N, M <= 1000000000, M >= 文本中最长的单词的长度,文本长度不超过100个字符。

输出

最后一个字符的行号和列号。

样例输入

2 9
Good good study day day up

样例输出

7 7

 

import java.util.Scanner;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;

public class Main {
	static int m, n;
	static int N=1000+10;
	static long count[]=new long[N];
	static int head[]=new int[N], tail[]=new int[N];
	static int wordlen[]=new int[N],
			   wc/*单词总数*/, 
			   start, end/*循环开始与结束行号*/,
			   headword[]=new int[N], 
			   tl/*所有单词总长度*/;
	static long totline;//总行数,列数

	static public void main(String args[])
	{
		Scanner cin = new Scanner(System.in);
		wc=0;
		n = cin.nextInt();
		m = cin.nextInt();
		tl=0;
		while(cin.hasNext())
		{
			String s = cin.next();
			wordlen[wc++]=s.length();
			tl+=s.length();
			//System.out.println(s);
		}
		//System.out.println(wc);
		getCycle();
		//System.out.println(start+"-"+end);
		int line = findLastLine();
		int col = findCol(line);

		System.out.println(totline+" "+col);
	}

	//计算循环
	static public void getCycle()
	{
		Map<Integer, Integer> cycleline = new HashMap();
		tail[0]=1;
		count[0]=0;
		int cur=0, line=0;

		while(true)
		{
			line++;
			head[line]=tail[line-1]^1;
			count[line]=count[line-1];
			int len=m;

			headword[line]=cur;
			//System.out.println(line + "|tail:" + head[line] + ", len :" + len);
			if(len-head[line]<wordlen[cur])
			{
				tail[line]=1;
				continue;
			}
			len-= head[line]+wordlen[cur];
			//System.out.println(line + "|tail:" + tail[line] + ", len :" + len);
			cur=(cur+1) % wc;
			if(cur==0)++count[line];

			count[line] += len/(tl + wc); //单行有循环直接除掉,否则复杂度高
			len %= (tl+wc);

			while(len>wordlen[cur]) //一个一个往里加
			{
				len -= 1+wordlen[cur];
				cur = (cur+1)%wc;
				if(cur==0)++count[line];
			}

			tail[line]=(len>0) ? 1:0;
			//System.out.println(line + "|tail:" + tail[line] + ", len :" + len);
			//判断是否则重复有循环了
			//这里利用行首单词与行首是否有空格作为判断依据
			if(cycleline.containsKey(headword[line]*10 + head[line])){
				//System.out.println("c:" + line + "|" + (headword[line]*10 + head[line]) );
				break;
			}
			cycleline.put(headword[line]*10 + head[line], line);
		}

		start = cycleline.get(headword[line]*10 + head[line]);
		end = line-1;
	}

	//获取最后一行在循环中的位置, 以总行数
	static public int findLastLine()
	{
		int i=0;
		for(i=1;i<start && count[i]<n; ++i);
		n-= count[i-1];
		totline=i;
		if(i<start) return i;//还没有到重复的地方就结束了
		long cyclenum = count[end]-count[start-1];
		long cyclelines = end-start+1;
		long sum=start-1+((n-1)/cyclenum)*cyclelines;//起始加上中间重复的

		//剩下一小段未重复的
		n-= (n-1)/cyclenum*cyclenum;
		for(i=start; count[i]-count[start-1]<n;++i);
		n -= count[i-1]-count[start-1];//剩下n遍
		totline=sum+i-start+1;
		return i;
	}

	//获取列数
	static public int findCol(int line)
	{
		int len = head[line];
		int cur = headword[line];
		len += wordlen[cur];
		cur = (cur+1)%wc;

		if(cur==0) n--;

		if(n>1)
		{
			len += (n-1)*(tl + wc);
			n=1;
		}

		while(n>0)
		{
			len += 1+wordlen[cur];

			cur = (cur+1)%wc;
			if(cur==0)--n;
		}

		return len;
	}
}

/*
test cases

1000000001 9
Good good study day day up

1000000000 1000000000
Good good study day day up

2 1000000000
Good good study day day up

1 9
a b c dd ee f g hhhhh

2 9
a b c dd ee f g hhhhh

3 9
a b c dd ee f g hhhhh 

1000 1
a

1 5
a b c dd ee f g hhhhh iiiii

*/

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值